Line data Source code
1 : /*
2 : Authors:
3 : Jakub Hrozek <jhrozek@redhat.com>
4 :
5 : Copyright (C) 2014 Red Hat
6 :
7 : SSSD tests: Resolver tests using a fake resolver library
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <talloc.h>
24 : #include <tevent.h>
25 : #include <errno.h>
26 : #include <popt.h>
27 : #include <arpa/inet.h>
28 : #include <netinet/in.h>
29 : #include <sys/types.h>
30 : #include <stdarg.h>
31 : #include <stdlib.h>
32 :
33 : #include <resolv.h>
34 :
35 : #include "tests/cmocka/common_mock.h"
36 : #include "tests/cmocka/common_mock_resp.h"
37 :
38 : #define TEST_BUFSIZE 1024
39 : #define TEST_DEFAULT_TIMEOUT 5
40 : #define TEST_SRV_QUERY "_ldap._tcp.sssd.com"
41 :
42 : static TALLOC_CTX *global_mock_context = NULL;
43 :
44 : struct srv_rrdata {
45 : uint16_t port;
46 : uint16_t prio;
47 : uint16_t weight;
48 : uint32_t ttl;
49 : const char *hostname;
50 : };
51 :
52 1 : static ssize_t dns_header(unsigned char **buf, size_t ancount)
53 : {
54 : uint8_t *hb;
55 : HEADER h;
56 :
57 1 : hb = *buf;
58 1 : memset(hb, 0, NS_HFIXEDSZ);
59 1 : memset(&h, 0, sizeof(h));
60 :
61 1 : h.id = res_randomid(); /* random query ID */
62 1 : h.qr = 1; /* response flag */
63 1 : h.rd = 1; /* recursion desired */
64 1 : h.ra = 1; /* resursion available */
65 :
66 1 : h.qdcount = htons(1); /* no. of questions */
67 1 : h.ancount = htons(ancount); /* no. of answers */
68 1 : h.arcount = htons(0); /* no. of add'tl records */
69 1 : memcpy(hb, &h, sizeof(h));
70 :
71 1 : hb += NS_HFIXEDSZ; /* move past the header */
72 1 : *buf = hb;
73 :
74 1 : return NS_HFIXEDSZ;
75 : }
76 :
77 1 : static ssize_t dns_question(const char *question,
78 : uint16_t type,
79 : uint8_t **question_ptr,
80 : size_t remaining)
81 : {
82 1 : unsigned char *qb = *question_ptr;
83 : int n;
84 :
85 1 : n = ns_name_compress(question, qb, remaining, NULL, NULL);
86 1 : assert_true(n > 0);
87 :
88 1 : qb += n;
89 1 : remaining -= n;
90 :
91 1 : NS_PUT16(type, qb);
92 1 : NS_PUT16(ns_c_in, qb);
93 :
94 1 : *question_ptr = qb;
95 1 : return n + 2 * sizeof(uint16_t);
96 : }
97 :
98 2 : static ssize_t add_rr_common(uint16_t type,
99 : uint32_t ttl,
100 : size_t rdata_size,
101 : const char *key,
102 : size_t remaining,
103 : uint8_t **rdata_ptr)
104 : {
105 2 : uint8_t *rd = *rdata_ptr;
106 2 : ssize_t written = 0;
107 :
108 2 : written = ns_name_compress(key, rd, remaining, NULL, NULL);
109 2 : assert_int_not_equal(written, -1);
110 2 : rd += written;
111 2 : remaining -= written;
112 :
113 2 : assert_true(remaining > 3 * sizeof(uint16_t) + sizeof(uint32_t));
114 2 : NS_PUT16(type, rd);
115 2 : NS_PUT16(ns_c_in, rd);
116 2 : NS_PUT32(ttl, rd);
117 2 : NS_PUT16(rdata_size, rd);
118 :
119 2 : assert_true(remaining > rdata_size);
120 2 : *rdata_ptr = rd;
121 2 : return written + 3 * sizeof(uint16_t) + sizeof(uint32_t) + rdata_size;
122 : }
123 :
124 2 : static ssize_t add_srv_rr(struct srv_rrdata *rr,
125 : const char *question,
126 : uint8_t *answer,
127 : size_t anslen)
128 : {
129 2 : uint8_t *a = answer;
130 : ssize_t resp_size;
131 : size_t rdata_size;
132 : unsigned char hostname_compressed[MAXDNAME];
133 : ssize_t compressed_len;
134 :
135 2 : rdata_size = 3 * sizeof(uint16_t);
136 :
137 : /* Prepare the data to write */
138 2 : compressed_len = ns_name_compress(rr->hostname,
139 : hostname_compressed, MAXDNAME,
140 : NULL, NULL);
141 2 : assert_int_not_equal(compressed_len, -1);
142 2 : rdata_size += compressed_len;
143 :
144 2 : resp_size = add_rr_common(ns_t_srv, rr->ttl, rdata_size,
145 : question, anslen, &a);
146 :
147 2 : NS_PUT16(rr->prio, a);
148 2 : NS_PUT16(rr->weight, a);
149 2 : NS_PUT16(rr->port, a);
150 2 : memcpy(a, hostname_compressed, compressed_len);
151 :
152 2 : return resp_size;
153 : }
154 :
155 1 : unsigned char *create_srv_buffer(TALLOC_CTX *mem_ctx,
156 : const char *question,
157 : struct srv_rrdata *rrs,
158 : size_t n_rrs,
159 : size_t *_buflen)
160 : {
161 : unsigned char *buf;
162 : unsigned char *buf_head;
163 : ssize_t len;
164 : ssize_t i;
165 1 : ssize_t total = 0;
166 :
167 1 : buf = talloc_zero_array(mem_ctx, unsigned char, TEST_BUFSIZE);
168 1 : assert_non_null(buf);
169 1 : buf_head = buf;
170 :
171 1 : len = dns_header(&buf, n_rrs);
172 1 : assert_true(len > 0);
173 1 : total += len;
174 :
175 1 : len = dns_question(question, ns_t_srv, &buf, TEST_BUFSIZE - total);
176 1 : assert_true(len > 0);
177 1 : total += len;
178 :
179 : /* answer */
180 3 : for (i = 0; i < n_rrs; i++) {
181 2 : len = add_srv_rr(&rrs[i], question, buf, TEST_BUFSIZE - total);
182 2 : assert_true(len > 0);
183 2 : total += len;
184 2 : buf += len;
185 : }
186 :
187 1 : *_buflen = total;
188 1 : return buf_head;
189 : }
190 :
191 : struct fake_ares_query {
192 : int status;
193 : int timeouts;
194 : unsigned char *abuf;
195 : int alen;
196 : };
197 :
198 1 : void mock_ares_query(int status, int timeouts, unsigned char *abuf, int alen)
199 : {
200 1 : will_return(__wrap_ares_query, status);
201 1 : will_return(__wrap_ares_query, timeouts);
202 1 : will_return(__wrap_ares_query, abuf);
203 1 : will_return(__wrap_ares_query, alen);
204 1 : }
205 :
206 1 : void __wrap_ares_query(ares_channel channel, const char *name, int dnsclass,
207 : int type, ares_callback callback, void *arg)
208 : {
209 : struct fake_ares_query query;
210 :
211 1 : query.status = sss_mock_type(int);
212 1 : query.timeouts = sss_mock_type(int);
213 1 : query.abuf = sss_mock_ptr_type(unsigned char *);
214 1 : query.alen = sss_mock_type(int);
215 :
216 1 : callback(arg, query.status, query.timeouts, query.abuf, query.alen);
217 1 : }
218 :
219 : /* The unit test */
220 : struct resolv_fake_ctx {
221 : struct resolv_ctx *resolv;
222 : struct sss_test_ctx *ctx;
223 : };
224 :
225 1 : static int test_resolv_fake_setup(void **state)
226 : {
227 : struct resolv_fake_ctx *test_ctx;
228 : int ret;
229 :
230 1 : assert_true(leak_check_setup());
231 1 : global_mock_context = talloc_new(global_talloc_context);
232 1 : assert_non_null(global_mock_context);
233 :
234 1 : test_ctx = talloc_zero(global_mock_context,
235 : struct resolv_fake_ctx);
236 1 : assert_non_null(test_ctx);
237 :
238 1 : test_ctx->ctx = create_ev_test_ctx(test_ctx);
239 1 : assert_non_null(test_ctx->ctx);
240 :
241 1 : ret = resolv_init(test_ctx, test_ctx->ctx->ev,
242 : TEST_DEFAULT_TIMEOUT, &test_ctx->resolv);
243 1 : assert_int_equal(ret, EOK);
244 :
245 1 : *state = test_ctx;
246 1 : return 0;
247 : }
248 :
249 1 : static int test_resolv_fake_teardown(void **state)
250 : {
251 1 : struct resolv_fake_ctx *test_ctx =
252 1 : talloc_get_type(*state, struct resolv_fake_ctx);
253 :
254 1 : talloc_free(test_ctx);
255 1 : talloc_free(global_mock_context);
256 1 : assert_true(leak_check_teardown());
257 1 : return 0;
258 : }
259 :
260 1 : void test_resolv_fake_srv_done(struct tevent_req *req)
261 : {
262 : errno_t ret;
263 : TALLOC_CTX *tmp_ctx;
264 : int status;
265 : uint32_t ttl;
266 1 : struct ares_srv_reply *srv_replies = NULL;
267 1 : struct resolv_fake_ctx *test_ctx =
268 1 : tevent_req_callback_data(req, struct resolv_fake_ctx);
269 :
270 1 : tmp_ctx = talloc_new(test_ctx);
271 1 : assert_non_null(tmp_ctx);
272 :
273 1 : ret = resolv_getsrv_recv(tmp_ctx, req, &status, NULL,
274 : &srv_replies, &ttl);
275 1 : assert_int_equal(ret, EOK);
276 :
277 1 : assert_non_null(srv_replies);
278 1 : assert_int_equal(srv_replies->priority, 1);
279 1 : assert_int_equal(srv_replies->weight, 40);
280 1 : assert_int_equal(srv_replies->port, 389);
281 1 : assert_string_equal(srv_replies->host, "ldap.sssd.com");
282 :
283 1 : srv_replies = srv_replies->next;
284 1 : assert_non_null(srv_replies);
285 1 : assert_int_equal(srv_replies->priority, 1);
286 1 : assert_int_equal(srv_replies->weight, 60);
287 1 : assert_int_equal(srv_replies->port, 389);
288 1 : assert_string_equal(srv_replies->host, "ldap2.sssd.com");
289 :
290 1 : srv_replies = srv_replies->next;
291 1 : assert_null(srv_replies);
292 :
293 1 : assert_int_equal(ttl, 500);
294 :
295 1 : talloc_free(tmp_ctx);
296 1 : test_ev_done(test_ctx->ctx, EOK);
297 1 : }
298 :
299 1 : void test_resolv_fake_srv(void **state)
300 : {
301 : int ret;
302 : struct tevent_req *req;
303 1 : struct resolv_fake_ctx *test_ctx =
304 1 : talloc_get_type(*state, struct resolv_fake_ctx);
305 :
306 : unsigned char *buf;
307 : size_t buflen;
308 :
309 : struct srv_rrdata rr[2];
310 :
311 1 : rr[0].prio = 1;
312 1 : rr[0].port = 389;
313 1 : rr[0].weight = 40;
314 1 : rr[0].ttl = 600;
315 1 : rr[0].hostname = "ldap.sssd.com";
316 :
317 1 : rr[1].prio = 1;
318 1 : rr[1].port = 389;
319 1 : rr[1].weight = 60;
320 1 : rr[1].ttl = 500;
321 1 : rr[1].hostname = "ldap2.sssd.com";
322 :
323 1 : buf = create_srv_buffer(test_ctx, TEST_SRV_QUERY, rr, 2, &buflen);
324 1 : assert_non_null(buf);
325 1 : mock_ares_query(0, 0, buf, buflen);
326 :
327 1 : req = resolv_getsrv_send(test_ctx, test_ctx->ctx->ev,
328 : test_ctx->resolv, TEST_SRV_QUERY);
329 1 : assert_non_null(req);
330 1 : tevent_req_set_callback(req, test_resolv_fake_srv_done, test_ctx);
331 :
332 1 : ret = test_ev_loop(test_ctx->ctx);
333 1 : assert_int_equal(ret, ERR_OK);
334 1 : }
335 :
336 1 : int main(int argc, const char *argv[])
337 : {
338 : int rv;
339 : poptContext pc;
340 : int opt;
341 6 : struct poptOption long_options[] = {
342 : POPT_AUTOHELP
343 5 : SSSD_DEBUG_OPTS
344 : POPT_TABLEEND
345 : };
346 :
347 1 : const struct CMUnitTest tests[] = {
348 : cmocka_unit_test_setup_teardown(test_resolv_fake_srv,
349 : test_resolv_fake_setup,
350 : test_resolv_fake_teardown),
351 : };
352 :
353 : /* Set debug level to invalid value so we can deside if -d 0 was used. */
354 1 : debug_level = SSSDBG_INVALID;
355 :
356 1 : pc = poptGetContext(argv[0], argc, argv, long_options, 0);
357 1 : while((opt = poptGetNextOpt(pc)) != -1) {
358 : switch(opt) {
359 : default:
360 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
361 : poptBadOption(pc, 0), poptStrerror(opt));
362 0 : poptPrintUsage(pc, stderr, 0);
363 0 : return 1;
364 : }
365 : }
366 1 : poptFreeContext(pc);
367 :
368 1 : DEBUG_CLI_INIT(debug_level);
369 :
370 : /* Even though normally the tests should clean up after themselves
371 : * they might not after a failed run. Remove the old db to be sure */
372 1 : tests_set_cwd();
373 :
374 1 : rv = cmocka_run_group_tests(tests, NULL, NULL);
375 1 : return rv;
376 : }
|