1 /* check-sources:disable-copyright-check */
2 #include <droplet.h>
3 #include <check.h>
4 #include <arpa/inet.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include "utest_main.h"
8
9 dpl_status_t dpl_addrlist_get_nth(dpl_addrlist_t* addrlist,
10 int n,
11 dpl_addr_t** addrp);
12
13 /* create an empty addrlist */
START_TEST(empty_test)14 START_TEST(empty_test)
15 {
16 dpl_addrlist_t* addrlist;
17 dpl_addr_t* addrp = NULL;
18 char* s = NULL;
19
20 addrlist = dpl_addrlist_create("4244");
21 dpl_assert_ptr_not_null(addrlist);
22
23 /* an empty addrlist has a zero count */
24 dpl_assert_int_eq(0, dpl_addrlist_count(addrlist));
25
26 /* an empty addrlist has no elements to get */
27 dpl_assert_int_eq(DPL_ENOENT, dpl_addrlist_get_nth(addrlist, 0, &addrp));
28
29 /* an empty addrlist is described as a non-NULL empty string */
30 s = dpl_addrlist_get(addrlist);
31 dpl_assert_str_eq(s, "");
32
33 free(s);
34 dpl_addrlist_free(addrlist);
35 }
36 END_TEST
37
START_TEST(default_default_port_test)38 START_TEST(default_default_port_test)
39 {
40 dpl_addrlist_t* addrlist;
41 dpl_status_t r;
42 char* s;
43
44 /* create the addrlist */
45 addrlist = dpl_addrlist_create(NULL);
46 dpl_assert_ptr_not_null(addrlist);
47
48 r = dpl_addrlist_add_from_str(addrlist, "192.168.1.1");
49 dpl_assert_int_eq(DPL_SUCCESS, r);
50 dpl_assert_int_eq(1, dpl_addrlist_count(addrlist));
51
52 /* verify the string form of the addrlist */
53 s = dpl_addrlist_get(addrlist);
54 dpl_assert_str_eq(s, "192.168.1.1:80");
55
56 free(s);
57 dpl_addrlist_free(addrlist);
58 }
59 END_TEST
60
START_TEST(multiple_add_test)61 START_TEST(multiple_add_test)
62 {
63 dpl_addrlist_t* addrlist;
64 dpl_status_t r;
65
66 /* create the addrlist */
67 addrlist = dpl_addrlist_create(NULL);
68 dpl_assert_ptr_not_null(addrlist);
69
70 /* add an address */
71 r = dpl_addrlist_add_from_str(addrlist, "192.168.1.1:80");
72 dpl_assert_int_eq(DPL_SUCCESS, r);
73 dpl_assert_int_eq(1, dpl_addrlist_count(addrlist));
74
75 /* adding the same address again is a no-op (well not
76 * quite but close enough). */
77 r = dpl_addrlist_add_from_str(addrlist, "192.168.1.1:80");
78 dpl_assert_int_eq(DPL_SUCCESS, r);
79 dpl_assert_int_eq(1, dpl_addrlist_count(addrlist));
80
81 /* an address which is the same IP but a different
82 * port counts as a different address */
83 r = dpl_addrlist_add_from_str(addrlist, "192.168.1.1:8080");
84 dpl_assert_int_eq(DPL_SUCCESS, r);
85 dpl_assert_int_eq(2, dpl_addrlist_count(addrlist));
86
87 dpl_addrlist_free(addrlist);
88 }
89 END_TEST
90
START_TEST(create_from_str_1_test)91 START_TEST(create_from_str_1_test)
92 {
93 dpl_addrlist_t* addrlist;
94 dpl_addr_t* addrp = NULL;
95 dpl_status_t r;
96 char *s, ident[INET_ADDRSTRLEN];
97
98 /* create the addrlist */
99 addrlist = dpl_addrlist_create_from_str("4244", "192.168.1.1");
100 dpl_assert_ptr_not_null(addrlist);
101
102 /* verify length */
103 dpl_assert_int_eq(1, dpl_addrlist_count(addrlist));
104
105 /* verify getting elements by index */
106 r = dpl_addrlist_get_nth(addrlist, 0, &addrp);
107 dpl_assert_int_eq(DPL_SUCCESS, r);
108 dpl_assert_ptr_not_null(addrp);
109 dpl_assert_str_eq(addrp->host, "192.168.1.1");
110 dpl_assert_str_eq(addrp->portstr, "4244");
111 dpl_assert_ptr_not_null(addrp->h);
112 dpl_assert_int_eq(addrp->h->h_addrtype, AF_INET);
113 inet_ntop(addrp->h->h_addrtype, addrp->h->h_addr, ident, sizeof(ident));
114 dpl_assert_str_eq(ident, "192.168.1.1");
115 dpl_assert_int_eq(addrp->port, 4244);
116
117 /* indexes wrap modulo the count */
118 r = dpl_addrlist_get_nth(addrlist, 1, &addrp);
119 dpl_assert_ptr_not_null(addrp);
120 dpl_assert_str_eq(addrp->host, "192.168.1.1");
121 dpl_assert_str_eq(addrp->portstr, "4244");
122 dpl_assert_ptr_not_null(addrp->h);
123 dpl_assert_int_eq(addrp->h->h_addrtype, AF_INET);
124 inet_ntop(addrp->h->h_addrtype, addrp->h->h_addr, ident, sizeof(ident));
125 dpl_assert_str_eq(ident, "192.168.1.1");
126 dpl_assert_int_eq(addrp->port, 4244);
127
128 r = dpl_addrlist_get_nth(addrlist, 347, &addrp);
129 dpl_assert_ptr_not_null(addrp);
130 dpl_assert_str_eq(addrp->host, "192.168.1.1");
131 dpl_assert_str_eq(addrp->portstr, "4244");
132 dpl_assert_ptr_not_null(addrp->h);
133 dpl_assert_int_eq(addrp->h->h_addrtype, AF_INET);
134 inet_ntop(addrp->h->h_addrtype, addrp->h->h_addr, ident, sizeof(ident));
135 dpl_assert_str_eq(ident, "192.168.1.1");
136 dpl_assert_int_eq(addrp->port, 4244);
137
138 /* verify the string form of the addrlist */
139 s = dpl_addrlist_get(addrlist);
140 dpl_assert_str_eq(s, "192.168.1.1:4244");
141
142 free(s);
143 dpl_addrlist_free(addrlist);
144 }
145 END_TEST
146
START_TEST(create_from_str_3_test)147 START_TEST(create_from_str_3_test)
148 {
149 dpl_addrlist_t* addrlist;
150 dpl_addr_t* addrp = NULL;
151 dpl_status_t r;
152 int i;
153 char *s, ident[INET_ADDRSTRLEN];
154 int port_position[3] = {0, 0, 0};
155 int port_counts[3] = {0, 0, 0};
156 char expstr[256] = "";
157
158 /* create the addrlist */
159 addrlist = dpl_addrlist_create_from_str(
160 "4244", "192.168.1.1:4242,192.168.1.2:4243,192.168.1.3");
161 dpl_assert_ptr_not_null(addrlist);
162
163 /* verify length */
164 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
165
166 /* verify getting elements by index */
167 /* indexes wrap modulo the count */
168
169 for (i = 0; i < 12; i++) {
170 r = dpl_addrlist_get_nth(addrlist, i, &addrp);
171 dpl_assert_int_eq(DPL_SUCCESS, r);
172 dpl_assert_ptr_not_null(addrp);
173 dpl_assert_ptr_not_null(addrp->h);
174
175 fail_unless(addrp->port >= 4242, NULL);
176 fail_unless(addrp->port <= 4244, NULL);
177
178 inet_ntop(addrp->h->h_addrtype, addrp->h->h_addr, ident, sizeof(ident));
179
180 if (addrp->port == 4242) {
181 dpl_assert_str_eq(addrp->host, "192.168.1.1");
182 dpl_assert_str_eq(addrp->portstr, "4242");
183 dpl_assert_str_eq(ident, "192.168.1.1");
184 port_counts[0]++;
185 } else if (addrp->port == 4243) {
186 dpl_assert_str_eq(addrp->host, "192.168.1.2");
187 dpl_assert_str_eq(addrp->portstr, "4243");
188 dpl_assert_str_eq(ident, "192.168.1.2");
189 port_counts[1]++;
190 } else /* 4244 */
191 {
192 dpl_assert_str_eq(addrp->host, "192.168.1.3");
193 dpl_assert_str_eq(addrp->portstr, "4244");
194 dpl_assert_str_eq(ident, "192.168.1.3");
195 port_counts[2]++;
196 }
197
198 if (port_position[i % 3])
199 dpl_assert_int_eq(port_position[i % 3], addrp->port);
200 else
201 port_position[i % 3] = addrp->port;
202 }
203 dpl_assert_int_eq(port_counts[0], 4);
204 dpl_assert_int_eq(port_counts[1], 4);
205 dpl_assert_int_eq(port_counts[2], 4);
206
207 /* verify the string form of the addrlist */
208 /* this is tough because the list is randomised internally */
209
210 for (i = 0; i < 3; i++) {
211 if (i) strcat(expstr, ",");
212 if (port_position[i] == 4242)
213 strcat(expstr, "192.168.1.1:4242");
214 else if (port_position[i] == 4243)
215 strcat(expstr, "192.168.1.2:4243");
216 else if (port_position[i] == 4244)
217 strcat(expstr, "192.168.1.3:4244");
218 }
219
220 s = dpl_addrlist_get(addrlist);
221 dpl_assert_str_eq(s, expstr);
222
223 free(s);
224 dpl_addrlist_free(addrlist);
225 }
226 END_TEST
227
228 /* Return a "host:port" pair for the i'th address in the addrlist */
get_nth(dpl_addrlist_t * addrlist,int i)229 static char* get_nth(dpl_addrlist_t* addrlist, int i)
230 {
231 dpl_addr_t* addrp = NULL;
232 dpl_status_t r;
233 char* addr;
234
235 r = dpl_addrlist_get_nth(addrlist, i, &addrp);
236 dpl_assert_int_eq(DPL_SUCCESS, r);
237 dpl_assert_ptr_not_null(addrp);
238 dpl_assert_ptr_not_null(addrp->h);
239
240 addr = malloc(DPL_ADDR_IDENT_STRLEN);
241 dpl_assert_ptr_not_null(addr);
242
243 return dpl_addr_get_ident(addrp->h, addrp->port, addr, DPL_ADDR_IDENT_STRLEN);
244 }
245
compare_addrs(const void * va,const void * vb)246 static int compare_addrs(const void* va, const void* vb)
247 {
248 return strcmp(*(const char**)va, *(const char**)vb);
249 }
250
discover_traversible(dpl_addrlist_t * addrlist)251 static char* discover_traversible(dpl_addrlist_t* addrlist)
252 {
253 int n;
254 int i;
255 int j;
256 char* s;
257 dpl_status_t r;
258 char* addr;
259 #define MAXADDRS 32
260 int naddrs = 0;
261 char* addrs[MAXADDRS];
262
263 n = dpl_addrlist_count(addrlist);
264 for (i = 0; i < n; i++) {
265 addr = get_nth(addrlist, i);
266
267 for (j = 0; j < naddrs; j++) {
268 if (!strcmp(addr, addrs[j])) break;
269 }
270 if (j == naddrs) {
271 dpl_assert_int_ne(naddrs, MAXADDRS);
272 addrs[naddrs++] = addr;
273 } else
274 free(addr);
275 }
276
277 /* sort the results into a predictable order */
278 qsort(addrs, naddrs, sizeof(char*), compare_addrs);
279
280 /* concatenate the strings into one big one */
281 n = 0;
282 for (i = 0; i < naddrs; i++) n += strlen(addrs[i]) + 1;
283
284 s = malloc(n);
285 dpl_assert_ptr_not_null(s);
286 s[0] = '\0';
287
288 for (i = 0; i < naddrs; i++) {
289 if (i) strcat(s, ",");
290 strcat(s, addrs[i]);
291 free(addrs[i]);
292 }
293
294 return s;
295 }
296
297
START_TEST(blacklist_test)298 START_TEST(blacklist_test)
299 {
300 dpl_addrlist_t* addrlist;
301 dpl_status_t r;
302 const char* exp;
303 char* act;
304 #define PORT "80"
305 #define ADDR1_H "192.168.1.1"
306 #define ADDR1 ADDR1_H ":" PORT
307 #define ADDR2_H "192.168.1.2"
308 #define ADDR2 ADDR2_H ":" PORT
309 #define ADDR3_H "192.168.1.3"
310 #define ADDR3 ADDR3_H ":" PORT
311
312 /* create the addrlist */
313 addrlist = dpl_addrlist_create_from_str(NULL, ADDR1 "," ADDR2 "," ADDR3);
314 dpl_assert_ptr_not_null(addrlist);
315 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
316 exp = ADDR1 "," ADDR2 "," ADDR3;
317 act = discover_traversible(addrlist);
318 dpl_assert_str_eq(exp, act);
319 free(act);
320
321 /* attempting to blacklist an unknown address fails safely */
322 r = dpl_addrlist_blacklist(addrlist, "192.168.1.32", PORT, 30 /*seconds*/);
323 dpl_assert_int_eq(DPL_ENOENT, r);
324 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
325 exp = ADDR1 "," ADDR2 "," ADDR3;
326 act = discover_traversible(addrlist);
327 dpl_assert_str_eq(exp, act);
328 free(act);
329
330 /* blacklisting an address succeeds and makes the address untraversible */
331 r = dpl_addrlist_blacklist(addrlist, ADDR2_H, PORT, 30 /*seconds*/);
332 dpl_assert_int_eq(DPL_SUCCESS, r);
333 /* note - count includes non-traversible addresses */
334 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
335 act = discover_traversible(addrlist);
336 exp = ADDR1 "," ADDR3;
337 dpl_assert_str_eq(exp, act);
338 free(act);
339
340 /* blacklisting the same address again is a no-op (well not
341 * quite but close enough). */
342 r = dpl_addrlist_blacklist(addrlist, ADDR2_H, PORT, 30 /*seconds*/);
343 dpl_assert_int_eq(DPL_SUCCESS, r);
344 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
345 act = discover_traversible(addrlist);
346 exp = ADDR1 "," ADDR3;
347 dpl_assert_str_eq(exp, act);
348 free(act);
349
350 /* un-blacklisting the address makes it traversible again */
351 r = dpl_addrlist_unblacklist(addrlist, ADDR2_H, PORT);
352 dpl_assert_int_eq(DPL_SUCCESS, r);
353 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
354 act = discover_traversible(addrlist);
355 exp = ADDR1 "," ADDR2 "," ADDR3;
356 dpl_assert_str_eq(exp, act);
357 free(act);
358
359 /* attempting to un-blacklist an unknown address fails safely */
360 r = dpl_addrlist_unblacklist(addrlist, "192.168.1.32", PORT);
361 dpl_assert_int_eq(DPL_ENOENT, r);
362 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
363 act = discover_traversible(addrlist);
364 exp = ADDR1 "," ADDR2 "," ADDR3;
365 dpl_assert_str_eq(exp, act);
366 free(act);
367
368 dpl_addrlist_free(addrlist);
369 #undef ADDR1_H
370 #undef ADDR1
371 #undef ADDR2_H
372 #undef ADDR2
373 #undef ADDR3_H
374 #undef ADDR3
375 #undef PORT
376 }
377 END_TEST
378
timeval_to_usec(const struct timeval * a)379 static uint64_t timeval_to_usec(const struct timeval* a)
380 {
381 return a->tv_sec * 1000000 + a->tv_usec;
382 }
383
elapsed_usec(const struct timeval * start)384 static uint64_t elapsed_usec(const struct timeval* start)
385 {
386 struct timeval now;
387 gettimeofday(&now, NULL);
388 return timeval_to_usec(&now) - timeval_to_usec(start);
389 }
390
391 #define SECONDS (1000000) /* in microseconds */
392 #define MILLISECONDS (1000) /* in microseconds */
393
394 /*
395 * Test that blacklisting is effective for a limited time period.
396 * Note that <check.h> provides no facility for doing time warps
397 * in the test process so we have to actually sleep.
398 */
START_TEST(blacklist_timeout_test)399 START_TEST(blacklist_timeout_test)
400 {
401 dpl_addrlist_t* addrlist;
402 unsigned long elapsed;
403 dpl_status_t r;
404 const char* exp;
405 char* act;
406 struct timeval start;
407 #define PORT "80"
408 #define ADDR1_H "192.168.1.1"
409 #define ADDR1 ADDR1_H ":" PORT
410 #define ADDR2_H "192.168.1.2"
411 #define ADDR2 ADDR2_H ":" PORT
412 #define ADDR3_H "192.168.1.3"
413 #define ADDR3 ADDR3_H ":" PORT
414
415 /* create the addrlist */
416 addrlist = dpl_addrlist_create_from_str(NULL, ADDR1 "," ADDR2 "," ADDR3);
417 dpl_assert_ptr_not_null(addrlist);
418 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
419 exp = ADDR1 "," ADDR2 "," ADDR3;
420 act = discover_traversible(addrlist);
421 dpl_assert_str_eq(exp, act);
422 free(act);
423
424 /* blacklisting an address succeeds */
425 gettimeofday(&start, NULL);
426 r = dpl_addrlist_blacklist(addrlist, ADDR2_H, PORT, 2 /*seconds*/);
427 dpl_assert_int_eq(DPL_SUCCESS, r);
428
429 while (elapsed_usec(&start) < 1 * SECONDS) {
430 // fprintf(stderr, "elapsed: %lu usec\n", elapsed_usec(&start));
431 // fflush(stderr);
432 /* address is not traversible before expiry */
433 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
434 act = discover_traversible(addrlist);
435 exp = ADDR1 "," ADDR3;
436 dpl_assert_str_eq(exp, act);
437 free(act);
438
439 usleep(100 * MILLISECONDS);
440 }
441
442 /* sleep a little more to make sure we're over the expiry */
443 usleep(1500 * MILLISECONDS);
444
445 /* address is traversible again after expiry */
446 dpl_assert_int_eq(3, dpl_addrlist_count(addrlist));
447 act = discover_traversible(addrlist);
448 exp = ADDR1 "," ADDR2 "," ADDR3;
449 dpl_assert_str_eq(exp, act);
450 free(act);
451
452 dpl_addrlist_free(addrlist);
453 #undef ADDR1_H
454 #undef ADDR1
455 #undef ADDR2_H
456 #undef ADDR2
457 #undef ADDR3_H
458 #undef ADDR3
459 #undef PORT
460 }
461 END_TEST
462
463 /* Passing addrlist=null to various functions fails cleanly */
START_TEST(null_test)464 START_TEST(null_test)
465 {
466 dpl_addr_t* addrp = NULL;
467 char* s;
468
469 /* dpl_addrlist_get() has an odd return for this corner case, but whatever */
470 s = dpl_addrlist_get(NULL);
471 dpl_assert_str_eq(s, "");
472 free(s);
473
474 dpl_assert_int_eq(0, dpl_addrlist_count(NULL));
475 dpl_addrlist_free(NULL);
476 dpl_assert_int_eq(DPL_FAILURE,
477 dpl_addrlist_blacklist(NULL, "192.168.1.32", "80", 30));
478 dpl_assert_int_eq(DPL_FAILURE,
479 dpl_addrlist_unblacklist(NULL, "192.168.1.32", "80"));
480 dpl_assert_int_eq(DPL_FAILURE,
481 dpl_addrlist_set_from_str(NULL, "192.168.1.32"));
482 dpl_assert_ptr_null(dpl_addrlist_create_from_str(NULL, NULL));
483 dpl_addrlist_lock(NULL);
484 dpl_addrlist_unlock(NULL);
485 dpl_assert_int_eq(DPL_ENOENT, dpl_addrlist_get_nth(NULL, 1, &addrp));
486 dpl_assert_int_eq(DPL_FAILURE, dpl_addrlist_add(NULL, "192.168.1.32", "80"));
487 }
488 END_TEST
489
490
addrlist_suite(void)491 Suite* addrlist_suite(void)
492 {
493 Suite* s = suite_create("addrlist");
494 TCase* t = tcase_create("base");
495 tcase_add_test(t, empty_test);
496 tcase_add_test(t, default_default_port_test);
497 tcase_add_test(t, multiple_add_test);
498 tcase_add_test(t, create_from_str_1_test);
499 tcase_add_test(t, create_from_str_3_test);
500 tcase_add_test(t, blacklist_test);
501 tcase_add_test(t, blacklist_timeout_test);
502 tcase_add_test(t, null_test);
503 suite_add_tcase(s, t);
504 return s;
505 }
506