1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2006 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**@internal
26  *
27  * @CFILE torture_sresolv.c Torture tests for Sofia resolver.
28  *
29  * @author Mikko Haataja
30  * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
31  */
32 
33 #include "config.h"
34 
35 #if HAVE_STDINT_H
36 #include <stdint.h>
37 #elif HAVE_INTTYPES_H
38 #include <inttypes.h>
39 #else
40 #if defined(_WIN32)
41 typedef unsigned _int8 uint8_t;
42 typedef unsigned _int16 uint16_t;
43 typedef unsigned _int32 uint32_t;
44 #endif
45 #endif
46 
47 #if HAVE_FCNTL_H
48 #include <fcntl.h>
49 #endif
50 
51 #if HAVE_NETINET_IN_H
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #endif
56 
57 #if HAVE_WINSOCK2_H
58 #include <winsock2.h>
59 #include <ws2tcpip.h>
60 #endif
61 
62 #include <sofia-resolv/sres.h>
63 #include <sofia-resolv/sres_async.h>
64 #include <sofia-resolv/sres_record.h>
65 #include <sofia-resolv/sres_cache.h>
66 
67 #include <sofia-sip/su_alloc.h>
68 
69 #include <time.h>
70 #include <assert.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <stdio.h>
74 #include <errno.h>
75 #include <unistd.h>
76 
77 #if HAVE_ALARM
78 #include <signal.h>
79 #endif
80 
81 #define TSTFLAGS tstflags
82 int tstflags, o_timing;
83 
84 #include <sofia-sip/tstdef.h>
85 
86 char const name[] = "torture_sresolv";
87 
88 struct sres_context_s
89 {
90   su_home_t        home[1];
91 };
92 
test_answer(sres_context_t * ctx,sres_query_t * q,sres_record_t ** answer)93 static void test_answer(sres_context_t *ctx,
94 			sres_query_t *q,
95 			sres_record_t **answer)
96 {
97 }
98 
99 static char name2048[2049] =
100   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
101   "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
102   "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
103   "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
104   "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
105   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
106   "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
107   "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
108 
109   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
110   "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
111   "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
112   "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
113   "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
114   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
115   "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
116   "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
117 
118   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
119   "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
120   "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
121   "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
122   "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
123   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
124   "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
125   "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
126 
127   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
128   "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
129   "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
130   "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
131   "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
132   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
133   "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
134   "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh";
135 
136 /* Test API function argument validation */
137 static
test_api_errors(void)138 int test_api_errors(void)
139 {
140   sres_context_t ctx[1];
141   sres_resolver_t *res;
142   int s, fd;
143   int sockets[20];
144   struct sockaddr sa[1] = {{ 0 }};
145   char *template = NULL;
146   FILE *f;
147 
148   BEGIN();
149 
150   memset(ctx, 0, sizeof ctx);
151 
152   template = su_sprintf(ctx->home, ".torture_sresolv_api.conf.XXXXXX");
153   TEST_1(template);
154 
155   TEST_1(res = sres_resolver_new(NULL));
156   TEST(su_home_threadsafe((su_home_t *)res), 0);
157   TEST_VOID(sres_resolver_unref(res));
158 
159 #ifndef _WIN32
160   fd = mkstemp(template); TEST_1(fd != -1);
161 #else
162   fd = open(template, O_WRONLY); TEST_1(fd != -1);
163 #endif
164 
165   f = fdopen(fd, "w"); TEST_1(f);
166   fprintf(f, "domain example.com\n");
167   fclose(f);
168 
169   /* Test also LOCALDOMAIN handling */
170   putenv("LOCALDOMAIN=localdomain");
171 
172   TEST_1(res = sres_resolver_new(template));
173   TEST(su_home_threadsafe((su_home_t *)res), 0);
174 
175   unlink(template);
176 
177   s = sockets[0];
178 
179   TEST_P(sres_resolver_ref(NULL), NULL);
180   TEST(errno, EFAULT);
181   sres_resolver_unref(NULL);
182 
183   TEST_P(sres_resolver_set_userdata(NULL, NULL), NULL);
184   TEST(errno, EFAULT);
185 
186   TEST_P(sres_resolver_get_userdata(NULL), NULL);
187 
188   TEST_P(sres_resolver_get_userdata(res), NULL);
189   TEST_P(sres_resolver_set_userdata(res, sa), NULL);
190   TEST_P(sres_resolver_get_userdata(res), sa);
191   TEST_P(sres_resolver_set_userdata(res, NULL), sa);
192   TEST_P(sres_resolver_get_userdata(res), NULL);
193 
194   errno = 0;
195   TEST_P(sres_query(NULL, test_answer, ctx, sres_type_a, "com"), NULL);
196   TEST(errno, EFAULT); errno = 0;
197   TEST_P(sres_query(res, test_answer, ctx, sres_type_a, NULL), NULL);
198   TEST(errno, EFAULT); errno = 0;
199   TEST_P(sres_query_sockaddr(res, test_answer, ctx,
200 			     sres_qtype_any, sa), NULL);
201   TEST(errno, EAFNOSUPPORT); errno = 0;
202 
203   TEST_P(sres_cached_answers(NULL, sres_qtype_any, "example.com"), NULL);
204   TEST(errno, EFAULT); errno = 0;
205   TEST_P(sres_cached_answers(res, sres_qtype_any, NULL), NULL);
206   TEST(errno, EFAULT); errno = 0;
207   TEST_P(sres_cached_answers(res, sres_qtype_any, name2048), NULL);
208   TEST(errno, ENAMETOOLONG); errno = 0;
209   TEST_P(sres_cached_answers_sockaddr(res, sres_qtype_any, sa), NULL);
210   TEST(errno, EAFNOSUPPORT); errno = 0;
211 
212   sres_free_answer(res, NULL);
213   sres_free_answers(res, NULL);
214   sres_sort_answers(res, NULL);
215 
216   sres_free_answer(NULL, NULL);
217   sres_free_answers(NULL, NULL);
218   sres_sort_answers(NULL, NULL);
219 
220   sres_resolver_unref(res);
221 
222   END();
223 }
224 
225 extern void sres_cache_clean(sres_cache_t *cache, time_t now);
226 
227 #ifndef CLOCK_PROCESS_CPUTIME_ID
228 #define CLOCK_PROCESS_CPUTIME_ID CLOCK_REALTIME
229 #endif
230 
231 static
test_cache(void)232 int test_cache(void)
233 {
234   BEGIN();
235 
236   sres_a_record_t *a, a0[1], **all;
237   sres_record_t **copy;
238   char host[128];
239   sres_cache_t *cache;
240   time_t now, base;
241   struct timespec t0, t1, t2;
242 
243   size_t i, N, N1 = 1000, N3 = 1000000;
244 
245   time(&base);
246 
247   cache = sres_cache_new(N1);
248   TEST_1(cache);
249 
250   all = calloc(N3, sizeof *all); if (!all) perror("calloc"), exit(2);
251 
252   memset(a0, 0, sizeof a0);
253 
254   a0->a_record->r_refcount = 1;
255   a0->a_record->r_size = sizeof *a;
256   a0->a_record->r_type = sres_type_a;
257   a0->a_record->r_class = sres_class_in;
258   a0->a_record->r_ttl = 3600;
259   a0->a_record->r_rdlen = sizeof a->a_addr;
260   a0->a_record->r_parsed = 1;
261 
262   for (i = 0, N = N3; i < N; i++) {
263     a0->a_record->r_name = host;
264 
265     snprintf(host, sizeof host, "%u.example.com.", (unsigned)i);
266 
267     a = (sres_a_record_t *)
268       sres_cache_alloc_record(cache, (sres_record_t *)a0, 0);
269 
270     if (!a)
271       perror("sres_cache_alloc_record"), exit(2);
272 
273     all[i] = a, a->a_record->r_refcount = 1;
274   }
275 
276   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
277 
278   for (i = 0, N = N3; i < N; i++) {
279     now = base + (3600 * i + N / 2) / N;
280     a->a_record->r_ttl = 60 + (i * 60) % 3600;
281     sres_cache_store(cache, (sres_record_t *)all[i], now);
282   }
283 
284   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
285   if (o_timing) {
286     t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
287     if (t1.tv_nsec < t0.tv_nsec)
288       t2.tv_sec--, t2.tv_nsec += 1000000000;
289     printf("sres_cache: stored %u entries: %lu.%09lu sec\n",
290 	   (unsigned)N, (long unsigned)t2.tv_sec, t2.tv_nsec);
291   }
292 
293   for (i = 0, N; i < N; i++)
294     TEST(all[i]->a_record->r_refcount, 2);
295 
296   TEST_1(copy = sres_cache_copy_answers(cache, (sres_record_t **)all));
297 
298   for (i = 0, N; i < N; i++)
299     TEST(all[i]->a_record->r_refcount, 3);
300 
301   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
302 
303   for (now = base; now <= base + 3660; now += 30)
304     sres_cache_clean(cache, now + 3600);
305 
306   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
307   if (o_timing) {
308     t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
309     if (t1.tv_nsec < t0.tv_nsec)
310       t2.tv_sec--, t2.tv_nsec += 1000000000;
311     printf("sres_cache: cleaned %u entries: %lu.%09lu sec\n",
312 	   (unsigned)N, (long unsigned)t2.tv_sec, t2.tv_nsec);
313   }
314 
315   for (i = 0, N; i < N; i++)
316     TEST(all[i]->a_record->r_refcount, 2);
317 
318   sres_cache_free_answers(cache, copy), copy = NULL;
319 
320   for (i = 0, N; i < N; i++)
321     TEST(all[i]->a_record->r_refcount, 1);
322 
323   base += 24 * 3600;
324 
325   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
326 
327   for (i = 0, N; i < N; i++) {
328     now = base + (3600 * i + N / 2) / N;
329     a->a_record->r_ttl = 60 + (i * 60) % 3600;
330     sres_cache_store(cache, (sres_record_t *)all[i], now);
331   }
332 
333   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
334   if (o_timing) {
335     t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
336     if (t1.tv_nsec < t0.tv_nsec)
337       t2.tv_sec--, t2.tv_nsec += 1000000000;
338     printf("sres_cache: stored %u entries: %lu.%09lu sec\n",
339 	   (unsigned)N, (long unsigned)t2.tv_sec, t2.tv_nsec);
340   }
341 
342   for (i = 0, N; i < N; i++)
343     TEST(all[i]->a_record->r_refcount, 2);
344 
345   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
346 
347   for (now = base; now <= base + 3660; now += 1)
348     sres_cache_clean(cache, now + 3600);
349 
350   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
351 
352   if (o_timing) {
353     t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
354     if (t1.tv_nsec < t0.tv_nsec)
355       t2.tv_sec--, t2.tv_nsec += 1000000000;
356     printf("sres_cache: cleaned %u entries: %lu.%09lu sec\n",
357 	   (unsigned)N, (long unsigned)t2.tv_sec, t2.tv_nsec);
358   }
359 
360   for (i = 0, N; i < N; i++) {
361     TEST(all[i]->a_record->r_refcount, 1);
362     sres_cache_free_one(cache, (sres_record_t *)all[i]);
363   }
364 
365   sres_cache_unref(cache);
366 
367   free(all);
368 
369   END();
370 }
371 
372 
373 #if HAVE_ALARM
sig_alarm(int s)374 static RETSIGTYPE sig_alarm(int s)
375 {
376   fprintf(stderr, "%s: FAIL! test timeout!\n", name);
377   exit(1);
378 }
379 #endif
380 
usage(int exitcode)381 void usage(int exitcode)
382 {
383   fprintf(stderr,
384 	  "usage: %s OPTIONS [-]\n"
385 	  "\twhere OPTIONS are\n"
386 	  "\t    -v be verbose\n"
387 	  "\t    -a abort on error\n"
388 	  "\t    -t show timing\n"
389 	  "\r    --no-alarm  disable timeout\n"
390 	  "\t    -llevel set debugging level\n",
391 	  name);
392   exit(exitcode);
393 }
394 
395 #include <sofia-sip/su_log.h>
396 
397 extern su_log_t sresolv_log[];
398 
main(int argc,char ** argv)399 int main(int argc, char **argv)
400 {
401   int i;
402   int error = 0;
403   int o_alarm = 1;
404 
405   for (i = 1; argv[i]; i++) {
406     if (argv[i][0] != '-')
407       break;
408     else if (strcmp(argv[i], "-") == 0) {
409       i++; break;
410     }
411     else if (strcmp(argv[i], "-v") == 0)
412       tstflags |= tst_verbatim;
413     else if (strcmp(argv[i], "-a") == 0)
414       tstflags |= tst_abort;
415     else if (strcmp(argv[i], "-t") == 0)
416       o_timing = 1;
417     else if (strcmp(argv[i], "--no-alarm") == 0) {
418       o_alarm = 0;
419     }
420     else if (strncmp(argv[i], "-l", 2) == 0) {
421       int level = 3;
422       char *rest = NULL;
423 
424       if (argv[i][2])
425 	level = strtol(argv[i] + 2, &rest, 10);
426       else if (argv[i + 1])
427 	level = strtol(argv[i + 1], &rest, 10), i++;
428       else
429 	level = 3, rest = "";
430 
431       if (rest == NULL || *rest)
432 	usage(1);
433 
434       su_log_set_level(sresolv_log, level);
435     }
436     else
437       usage(1);
438   }
439 
440 #if HAVE_ALARM
441   if (o_alarm) {
442     alarm(60);
443     signal(SIGALRM, sig_alarm);
444   }
445 #endif
446 
447   if (!(TSTFLAGS & tst_verbatim)) {
448     su_log_soft_set_level(sresolv_log, 0);
449   }
450 
451   error |= test_api_errors();
452   error |= test_cache();
453 
454   return error;
455 }
456