]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - testcode/testbound.c
Apply upstream fix 08968baec1122a58bb90d8f97ad948a75f8a5d69:
[FreeBSD/FreeBSD.git] / testcode / testbound.c
1 /*
2  * testcode/testbound.c - test program for unbound.
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 /**
37  * \file
38  * Exits with code 1 on a failure. 0 if all unit tests are successful.
39  */
40
41 #include "config.h"
42 #ifdef HAVE_TIME_H
43 #  include <time.h>
44 #endif
45 #include "testcode/testpkts.h"
46 #include "testcode/replay.h"
47 #include "testcode/fake_event.h"
48 #include "daemon/remote.h"
49 #include "util/config_file.h"
50 #include "sldns/keyraw.h"
51 #include <ctype.h>
52
53 /** signal that this is a testbound compile */
54 #define unbound_testbound 1
55 /** 
56  * include the main program from the unbound daemon.
57  * rename main to daemon_main to call it
58  */
59 #define main daemon_main
60 #include "daemon/unbound.c"
61 #undef main
62
63 /** maximum line length for lines in the replay file. */
64 #define MAX_LINE_LEN 1024
65 /** config files (removed at exit) */
66 static struct config_strlist* cfgfiles = NULL;
67
68 #ifdef UNBOUND_ALLOC_STATS
69 #  define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__)
70 char* unbound_stat_strdup_log(char* s, const char* file, int line,
71         const char* func);
72 char* unbound_stat_strdup_log(char* s, const char* file, int line,
73         const char* func) {
74         char* result;
75         size_t len;
76         if(!s) return NULL;
77         len = strlen(s);
78         log_info("%s:%d %s strdup(%u)", file, line, func, (unsigned)len+1);
79         result = unbound_stat_malloc(len+1);
80         memmove(result, s, len+1);
81         return result;
82 }
83 #endif /* UNBOUND_ALLOC_STATS */
84
85 /** give commandline usage for testbound. */
86 static void
87 testbound_usage(void)
88 {
89         printf("usage: testbound [options]\n");
90         printf("\ttest the unbound daemon.\n");
91         printf("-h      this help\n");
92         printf("-p file playback text file\n");
93         printf("-1      detect SHA1 support (exit code 0 or 1)\n");
94         printf("-2      detect SHA256 support (exit code 0 or 1)\n");
95         printf("-g      detect GOST support (exit code 0 or 1)\n");
96         printf("-e      detect ECDSA support (exit code 0 or 1)\n");
97         printf("-c      detect CLIENT_SUBNET support (exit code 0 or 1)\n");
98         printf("-i      detect IPSECMOD support (exit code 0 or 1)\n");
99         printf("-s      testbound self-test - unit test of testbound parts.\n");
100         printf("-o str  unbound commandline options separated by spaces.\n");
101         printf("Version %s\n", PACKAGE_VERSION);
102         printf("BSD licensed, see LICENSE file in source package.\n");
103         printf("Report bugs to %s.\n", PACKAGE_BUGREPORT);
104 }
105
106 /** Max number of arguments to pass to unbound. */
107 #define MAXARG 100
108
109 /** 
110  * Add options from string to passed argc. splits on whitespace.
111  * @param args: the option argument, "-v -p 12345" or so.
112  * @param pass_argc: ptr to the argc for unbound. Modified.
113  * @param pass_argv: the argv to pass to unbound. Modified.
114  */
115 static void
116 add_opts(const char* args, int* pass_argc, char* pass_argv[])
117 {
118         const char *p = args, *np;
119         size_t len;
120         while(p && isspace((unsigned char)*p)) 
121                 p++;
122         while(p && *p) {
123                 /* find location of next string and length of this one */
124                 if((np = strchr(p, ' ')))
125                         len = (size_t)(np-p);
126                 else    len = strlen(p);
127                 /* allocate and copy option */
128                 if(*pass_argc >= MAXARG-1)
129                         fatal_exit("too many arguments: '%s'", p);
130                 pass_argv[*pass_argc] = (char*)malloc(len+1);
131                 if(!pass_argv[*pass_argc])
132                         fatal_exit("add_opts: out of memory");
133                 memcpy(pass_argv[*pass_argc], p, len);
134                 pass_argv[*pass_argc][len] = 0;
135                 (*pass_argc)++;
136                 /* go to next option */
137                 p = np;
138                 while(p && isspace((unsigned char)*p)) 
139                         p++;
140         }
141 }
142
143 /** pretty print commandline for unbound in this test */
144 static void
145 echo_cmdline(int argc, char* argv[])
146 {
147         int i;
148         fprintf(stderr, "testbound is starting:");
149         for(i=0; i<argc; i++) {
150                 fprintf(stderr, " [%s]", argv[i]);
151         }
152         fprintf(stderr, "\n");
153 }
154
155 /** spool temp file name */
156 static void
157 spool_temp_file_name(int* lineno, FILE* cfg, char* id)
158 {
159         char line[MAX_LINE_LEN];
160         /* find filename for new file */
161         while(isspace((unsigned char)*id))
162                 id++;
163         if(*id == '\0') 
164                 fatal_exit("TEMPFILE_NAME must have id, line %d", *lineno);
165         id[strlen(id)-1]=0; /* remove newline */
166         fake_temp_file("_temp_", id, line, sizeof(line));
167         fprintf(cfg, "\"%s\"\n", line);
168 }
169
170 /** spool temp file */
171 static void
172 spool_temp_file(FILE* in, int* lineno, char* id)
173 {
174         char line[MAX_LINE_LEN];
175         char* parse;
176         FILE* spool;
177         /* find filename for new file */
178         while(isspace((unsigned char)*id))
179                 id++;
180         if(*id == '\0') 
181                 fatal_exit("TEMPFILE_CONTENTS must have id, line %d", *lineno);
182         id[strlen(id)-1]=0; /* remove newline */
183         fake_temp_file("_temp_", id, line, sizeof(line));
184         /* open file and spool to it */
185         spool = fopen(line, "w");
186         if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
187         fprintf(stderr, "testbound is spooling temp file: %s\n", line);
188         if(!cfg_strlist_insert(&cfgfiles, strdup(line))) 
189                 fatal_exit("out of memory");
190         line[sizeof(line)-1] = 0;
191         while(fgets(line, MAX_LINE_LEN-1, in)) {
192                 parse = line;
193                 (*lineno)++;
194                 while(isspace((unsigned char)*parse))
195                         parse++;
196                 if(strncmp(parse, "$INCLUDE_TEMPFILE", 17) == 0) {
197                         char l2[MAX_LINE_LEN-30]; /* -30 makes it fit with
198                                 a preceding $INCLUDE in the buf line[] */
199                         char* tid = parse+17;
200                         while(isspace((unsigned char)*tid))
201                                 tid++;
202                         tid[strlen(tid)-1]=0; /* remove newline */
203                         fake_temp_file("_temp_", tid, l2, sizeof(l2));
204                         snprintf(line, sizeof(line), "$INCLUDE %s\n", l2);
205                 }
206                 if(strncmp(parse, "TEMPFILE_END", 12) == 0) {
207                         fclose(spool);
208                         return;
209                 }
210                 fputs(line, spool);
211         }
212         fatal_exit("no TEMPFILE_END in input file");
213 }
214
215 /** spool autotrust file */
216 static void
217 spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id)
218 {
219         char line[MAX_LINE_LEN];
220         char* parse;
221         FILE* spool;
222         /* find filename for new file */
223         while(isspace((unsigned char)*id))
224                 id++;
225         if(*id == '\0') 
226                 fatal_exit("AUTROTRUST_FILE must have id, line %d", *lineno);
227         id[strlen(id)-1]=0; /* remove newline */
228         fake_temp_file("_auto_", id, line, sizeof(line));
229         /* add option for the file */
230         fprintf(cfg, "server:   auto-trust-anchor-file: \"%s\"\n", line);
231         /* open file and spool to it */
232         spool = fopen(line, "w");
233         if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
234         fprintf(stderr, "testbound is spooling key file: %s\n", line);
235         if(!cfg_strlist_insert(&cfgfiles, strdup(line))) 
236                 fatal_exit("out of memory");
237         line[sizeof(line)-1] = 0;
238         while(fgets(line, MAX_LINE_LEN-1, in)) {
239                 parse = line;
240                 (*lineno)++;
241                 while(isspace((unsigned char)*parse))
242                         parse++;
243                 if(strncmp(parse, "AUTOTRUST_END", 13) == 0) {
244                         fclose(spool);
245                         return;
246                 }
247                 fputs(line, spool);
248         }
249         fatal_exit("no AUTOTRUST_END in input file");
250 }
251
252 /** process config elements */
253 static void
254 setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
255 {
256         char configfile[MAX_LINE_LEN];
257         char line[MAX_LINE_LEN];
258         char* parse;
259         FILE* cfg;
260         fake_temp_file("_cfg", "", configfile, sizeof(configfile));
261         add_opts("-c", pass_argc, pass_argv);
262         add_opts(configfile, pass_argc, pass_argv);
263         cfg = fopen(configfile, "w");
264         if(!cfg) fatal_exit("could not open %s: %s", 
265                         configfile, strerror(errno));
266         if(!cfg_strlist_insert(&cfgfiles, strdup(configfile))) 
267                 fatal_exit("out of memory");
268         line[sizeof(line)-1] = 0;
269         /* some basic settings to not pollute the host system */
270         fprintf(cfg, "server:   use-syslog: no\n");
271         fprintf(cfg, "          directory: \"\"\n");
272         fprintf(cfg, "          chroot: \"\"\n");
273         fprintf(cfg, "          username: \"\"\n");
274         fprintf(cfg, "          pidfile: \"\"\n");
275         fprintf(cfg, "          val-log-level: 2\n");
276         fprintf(cfg, "remote-control:   control-enable: no\n");
277         while(fgets(line, MAX_LINE_LEN-1, in)) {
278                 parse = line;
279                 (*lineno)++;
280                 while(isspace((unsigned char)*parse))
281                         parse++;
282                 if(!*parse || parse[0] == ';')
283                         continue;
284                 if(strncmp(parse, "COMMANDLINE", 11) == 0) {
285                         parse[strlen(parse)-1] = 0; /* strip off \n */
286                         add_opts(parse+11, pass_argc, pass_argv);
287                         continue;
288                 }
289                 if(strncmp(parse, "AUTOTRUST_FILE", 14) == 0) {
290                         spool_auto_file(in, lineno, cfg, parse+14);
291                         continue;
292                 }
293                 if(strncmp(parse, "TEMPFILE_NAME", 13) == 0) {
294                         spool_temp_file_name(lineno, cfg, parse+13);
295                         continue;
296                 }
297                 if(strncmp(parse, "TEMPFILE_CONTENTS", 17) == 0) {
298                         spool_temp_file(in, lineno, parse+17);
299                         continue;
300                 }
301                 if(strncmp(parse, "CONFIG_END", 10) == 0) {
302                         fclose(cfg);
303                         return;
304                 }
305                 fputs(line, cfg);
306         }
307         fatal_exit("No CONFIG_END in input file");
308
309 }
310
311 /** read playback file */
312 static struct replay_scenario* 
313 setup_playback(const char* filename, int* pass_argc, char* pass_argv[])
314 {
315         struct replay_scenario* scen = NULL;
316         int lineno = 0;
317
318         if(filename) {
319                 FILE *in = fopen(filename, "rb");
320                 if(!in) {
321                         perror(filename);
322                         exit(1);
323                 }
324                 setup_config(in, &lineno, pass_argc, pass_argv);
325                 scen = replay_scenario_read(in, filename, &lineno);
326                 fclose(in);
327                 if(!scen)
328                         fatal_exit("Could not read: %s", filename);
329         }
330         else fatal_exit("need a playback file (-p)");
331         log_info("Scenario: %s", scen->title);
332         return scen;
333 }
334
335 /** remove config file at exit */
336 void remove_configfile(void)
337 {
338         struct config_strlist* p;
339         for(p=cfgfiles; p; p=p->next)
340                 unlink(p->str);
341         config_delstrlist(cfgfiles);
342         cfgfiles = NULL;
343 }
344
345 /**
346  * Main fake event test program. Setup, teardown and report errors.
347  * @param argc: arg count.
348  * @param argv: array of commandline arguments.
349  * @return program failure if test fails.
350  */
351 int 
352 main(int argc, char* argv[])
353 {
354         int c, res;
355         int pass_argc = 0;
356         char* pass_argv[MAXARG];
357         char* playback_file = NULL;
358         int init_optind = optind;
359         char* init_optarg = optarg;
360         struct replay_scenario* scen = NULL;
361
362         /* we do not want the test to depend on the timezone */
363         (void)putenv("TZ=UTC");
364         memset(pass_argv, 0, sizeof(pass_argv));
365 #ifdef HAVE_SYSTEMD
366         /* we do not want the test to use systemd daemon startup notification*/
367         (void)unsetenv("NOTIFY_SOCKET");
368 #endif /* HAVE_SYSTEMD */
369
370         log_init(NULL, 0, NULL);
371         /* determine commandline options for the daemon */
372         pass_argc = 1;
373         pass_argv[0] = "unbound";
374         add_opts("-d", &pass_argc, pass_argv);
375         while( (c=getopt(argc, argv, "12egciho:p:s")) != -1) {
376                 switch(c) {
377                 case 's':
378                         free(pass_argv[1]);
379                         testbound_selftest();
380                         checklock_stop();
381                         if(log_get_lock()) {
382                                 lock_basic_destroy((lock_basic_type*)log_get_lock());
383                         }
384                         exit(0);
385                 case '1':
386 #ifdef USE_SHA1
387                         printf("SHA1 supported\n");
388                         exit(0);
389 #else
390                         printf("SHA1 not supported\n");
391                         exit(1);
392 #endif
393                         break;
394                 case '2':
395 #if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2)
396                         printf("SHA256 supported\n");
397                         exit(0);
398 #else
399                         printf("SHA256 not supported\n");
400                         exit(1);
401 #endif
402                         break;
403                 case 'e':
404 #if defined(USE_ECDSA)
405                         printf("ECDSA supported\n");
406                         exit(0);
407 #else
408                         printf("ECDSA not supported\n");
409                         exit(1);
410 #endif
411                         break;
412                 case 'g':
413 #ifdef USE_GOST
414                         if(sldns_key_EVP_load_gost_id()) {
415                                 printf("GOST supported\n");
416                                 exit(0);
417                         } else {
418                                 printf("GOST not supported\n");
419                                 exit(1);
420                         }
421 #else
422                         printf("GOST not supported\n");
423                         exit(1);
424 #endif
425                         break;
426                 case 'c':
427 #ifdef CLIENT_SUBNET
428                         printf("CLIENT_SUBNET supported\n");
429                         exit(0);
430 #else
431                         printf("CLIENT_SUBNET not supported\n");
432                         exit(1);
433 #endif
434                         break;
435                 case 'i':
436 #ifdef USE_IPSECMOD
437                         printf("IPSECMOD supported\n");
438                         exit(0);
439 #else
440                         printf("IPSECMOD not supported\n");
441                         exit(1);
442 #endif
443                         break;
444                 case 'p':
445                         playback_file = optarg;
446                         break;
447                 case 'o':
448                         add_opts(optarg, &pass_argc, pass_argv);
449                         break;
450                 case '?':
451                 case 'h':
452                 default:
453                         testbound_usage();
454                         exit(1);
455                 }
456         }
457         argc -= optind;
458         /* argv += optind; not using further arguments */
459         if(argc != 0) {
460                 testbound_usage();
461                 exit(1);
462         }
463         log_info("Start of %s testbound program.", PACKAGE_STRING);
464         if(atexit(&remove_configfile) != 0)
465                 fatal_exit("atexit() failed: %s", strerror(errno));
466
467         /* setup test environment */
468         scen = setup_playback(playback_file, &pass_argc, pass_argv);
469         /* init fake event backend */
470         fake_event_init(scen);
471
472         pass_argv[pass_argc] = NULL;
473         echo_cmdline(pass_argc, pass_argv);
474
475         /* reset getopt processing */
476         optind = init_optind;
477         optarg = init_optarg;
478
479         /* run the normal daemon */
480         res = daemon_main(pass_argc, pass_argv);
481
482         fake_event_cleanup();
483         for(c=1; c<pass_argc; c++)
484                 free(pass_argv[c]);
485         if(res == 0) {
486                 log_info("Testbound Exit Success\n");
487                 /* remove configfile from here, the atexit() is for when
488                  * there is a crash to remove the tmpdir file.
489                  * This one removes the file while alloc and log locks are
490                  * still valid, and can be logged (for memory calculation),
491                  * it leaves the ptr NULL so the atexit does nothing. */
492                 remove_configfile();
493                 if(log_get_lock()) {
494                         lock_basic_destroy((lock_basic_type*)log_get_lock());
495                 }
496 #ifdef HAVE_PTHREAD
497                 /* dlopen frees its thread state (dlopen of gost engine) */
498                 pthread_exit(NULL);
499 #endif
500         }
501         return res;
502 }
503
504 /* fake remote control */
505 struct listen_port* daemon_remote_open_ports(struct config_file* 
506         ATTR_UNUSED(cfg))
507 {
508         return NULL;
509 }
510
511 struct daemon_remote* daemon_remote_create(struct config_file* ATTR_UNUSED(cfg))
512 {
513         return (struct daemon_remote*)calloc(1,1);
514 }
515
516 void daemon_remote_delete(struct daemon_remote* rc)
517 {
518         free(rc);
519 }
520
521 void daemon_remote_clear(struct daemon_remote* ATTR_UNUSED(rc))
522 {
523         /* nothing */
524 }
525
526 int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc),
527         struct listen_port* ATTR_UNUSED(ports), 
528         struct worker* ATTR_UNUSED(worker))
529 {
530         return 1;
531 }
532
533 int remote_accept_callback(struct comm_point* ATTR_UNUSED(c), 
534         void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
535         struct comm_reply* ATTR_UNUSED(repinfo))
536 {
537         log_assert(0);
538         return 0;
539 }
540
541 int remote_control_callback(struct comm_point* ATTR_UNUSED(c), 
542         void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
543         struct comm_reply* ATTR_UNUSED(repinfo))
544 {
545         log_assert(0);
546         return 0;
547 }
548
549 void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
550 {
551         log_assert(0);
552 }
553
554 void wsvc_command_option(const char* ATTR_UNUSED(wopt), 
555         const char* ATTR_UNUSED(cfgfile), int ATTR_UNUSED(v), 
556         int ATTR_UNUSED(c))
557 {
558         log_assert(0);
559 }
560
561 void wsvc_setup_worker(struct worker* ATTR_UNUSED(worker))
562 {
563         /* do nothing */
564 }
565
566 void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
567 {
568         /* do nothing */
569 }
570
571 #ifdef UB_ON_WINDOWS
572 void worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
573         void* ATTR_UNUSED(arg))
574 {
575         log_assert(0);
576 }
577
578 void wsvc_cron_cb(void* ATTR_UNUSED(arg))
579 {
580         log_assert(0);
581 }
582 #endif /* UB_ON_WINDOWS */
583
584 int tcp_connect_errno_needs_log(struct sockaddr* ATTR_UNUSED(addr),
585         socklen_t ATTR_UNUSED(addrlen))
586 {
587         return 1;
588 }
589
590 int squelch_err_ssl_handshake(unsigned long ATTR_UNUSED(err))
591 {
592         return 0;
593 }