Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[sfrench/samba-autobuild/.git] / source4 / torture / smbtorture.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006-2008
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/cmdline/popt_common.h"
23 #include "system/time.h"
24 #include "system/wait.h"
25 #include "system/filesys.h"
26 #include "system/readline.h"
27 #include "lib/smbreadline/smbreadline.h"
28 #include "libcli/libcli.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/events/events.h"
31 #include "dynconfig/dynconfig.h"
32
33 #include "torture/smbtorture.h"
34 #include "../lib/util/dlinklist.h"
35 #include "librpc/rpc/dcerpc.h"
36 #include "auth/gensec/gensec.h"
37 #include "param/param.h"
38
39 #include "auth/credentials/credentials.h"
40
41 static bool run_matching(struct torture_context *torture,
42                                                  const char *prefix, 
43                                                  const char *expr,
44                                                  struct torture_suite *suite,
45                                                  bool *matched)
46 {
47         bool ret = true;
48
49         if (suite == NULL) {
50                 struct torture_suite *o;
51
52                 for (o = (torture_root == NULL?NULL:torture_root->children); o; o = o->next) {
53                         if (gen_fnmatch(expr, o->name) == 0) {
54                                 *matched = true;
55                                 reload_charcnv(torture->lp_ctx);
56                                 ret &= torture_run_suite(torture, o);
57                                 continue;
58                         }
59
60                         ret &= run_matching(torture, o->name, expr, o, matched);
61                 }
62         } else {
63                 char *name;
64                 struct torture_suite *c;
65                 struct torture_tcase *t;
66
67                 for (c = suite->children; c; c = c->next) {
68                         asprintf(&name, "%s-%s", prefix, c->name);
69
70                         if (gen_fnmatch(expr, name) == 0) {
71                                 *matched = true;
72                                 reload_charcnv(torture->lp_ctx);
73                                 torture->active_testname = talloc_strdup(torture, prefix);
74                                 ret &= torture_run_suite(torture, c);
75                                 free(name);
76                                 continue;
77                         }
78                         
79                         ret &= run_matching(torture, name, expr, c, matched);
80
81                         free(name);
82                 }
83
84                 for (t = suite->testcases; t; t = t->next) {
85                         asprintf(&name, "%s-%s", prefix, t->name);
86                         if (gen_fnmatch(expr, name) == 0) {
87                                 *matched = true;
88                                 reload_charcnv(torture->lp_ctx);
89                                 torture->active_testname = talloc_strdup(torture, prefix);
90                                 ret &= torture_run_tcase(torture, t);
91                                 talloc_free(torture->active_testname);
92                         }
93                         free(name);
94                 }
95         }
96
97         return ret;
98 }
99
100 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
101
102 /****************************************************************************
103 run a specified test or "ALL"
104 ****************************************************************************/
105 static bool run_test(struct torture_context *torture, const char *name)
106 {
107         bool ret = true;
108         bool matched = false;
109         struct torture_suite *o;
110
111         if (strequal(name, "ALL")) {
112                 for (o = torture_root->children; o; o = o->next) {
113                         ret &= torture_run_suite(torture, o);
114                 }
115                 return ret;
116         }
117
118         ret = run_matching(torture, NULL, name, NULL, &matched);
119
120         if (!matched) {
121                 printf("Unknown torture operation '%s'\n", name);
122                 return false;
123         }
124
125         return ret;
126 }
127
128 static bool parse_target(struct loadparm_context *lp_ctx, const char *target)
129 {
130         char *host = NULL, *share = NULL;
131         struct dcerpc_binding *binding_struct;
132         NTSTATUS status;
133
134         /* see if its a RPC transport specifier */
135         if (!smbcli_parse_unc(target, NULL, &host, &share)) {
136                 status = dcerpc_parse_binding(talloc_autofree_context(), target, &binding_struct);
137                 if (NT_STATUS_IS_ERR(status)) {
138                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", target);
139                         return false;
140                 }
141                 lp_set_cmdline(lp_ctx, "torture:host", binding_struct->host);
142                 if (lp_parm_string(lp_ctx, NULL, "torture", "share") == NULL)
143                         lp_set_cmdline(lp_ctx, "torture:share", "IPC$");
144                 lp_set_cmdline(lp_ctx, "torture:binding", target);
145         } else {
146                 lp_set_cmdline(lp_ctx, "torture:host", host);
147                 lp_set_cmdline(lp_ctx, "torture:share", share);
148                 lp_set_cmdline(lp_ctx, "torture:binding", host);
149         }
150
151         return true;
152 }
153
154 static void parse_dns(struct loadparm_context *lp_ctx, const char *dns)
155 {
156         char *userdn, *basedn, *secret;
157         char *p, *d;
158
159         /* retrievieng the userdn */
160         p = strchr_m(dns, '#');
161         if (!p) {
162                 lp_set_cmdline(lp_ctx, "torture:ldap_userdn", "");
163                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
164                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
165                 return;
166         }
167         userdn = strndup(dns, p - dns);
168         lp_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn);
169
170         /* retrieve the basedn */
171         d = p + 1;
172         p = strchr_m(d, '#');
173         if (!p) {
174                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
175                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
176                 return;
177         }
178         basedn = strndup(d, p - d);
179         lp_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn);
180
181         /* retrieve the secret */
182         p = p + 1;
183         if (!p) {
184                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
185                 return;
186         }
187         secret = strdup(p);
188         lp_set_cmdline(lp_ctx, "torture:ldap_secret", secret);
189
190         printf ("%s - %s - %s\n", userdn, basedn, secret);
191
192 }
193
194 static void print_test_list(void)
195 {
196         struct torture_suite *o;
197         struct torture_suite *s;
198         struct torture_tcase *t;
199
200         if (torture_root == NULL)
201                 return;
202
203         for (o = torture_root->children; o; o = o->next) {
204                 for (s = o->children; s; s = s->next) {
205                         printf("%s-%s\n", o->name, s->name);
206                 }
207
208                 for (t = o->testcases; t; t = t->next) {
209                         printf("%s-%s\n", o->name, t->name);
210                 }
211         }
212 }
213
214 _NORETURN_ static void usage(poptContext pc)
215 {
216         struct torture_suite *o;
217         struct torture_suite *s;
218         struct torture_tcase *t;
219         int i;
220
221         poptPrintUsage(pc, stdout, 0);
222         printf("\n");
223
224         printf("The binding format is:\n\n");
225
226         printf("  TRANSPORT:host[flags]\n\n");
227
228         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
229         printf("  or ncalrpc for local connections.\n\n");
230
231         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
232         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
233         printf("  string.\n\n");
234
235         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
236         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
237         printf("  will be auto-determined.\n\n");
238
239         printf("  other recognised flags are:\n\n");
240
241         printf("    sign : enable ntlmssp signing\n");
242         printf("    seal : enable ntlmssp sealing\n");
243         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
244         printf("    validate: enable the NDR validator\n");
245         printf("    print: enable debugging of the packets\n");
246         printf("    bigendian: use bigendian RPC\n");
247         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
248
249         printf("  For example, these all connect to the samr pipe:\n\n");
250
251         printf("    ncacn_np:myserver\n");
252         printf("    ncacn_np:myserver[samr]\n");
253         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
254         printf("    ncacn_np:myserver[/pipe/samr]\n");
255         printf("    ncacn_np:myserver[samr,sign,print]\n");
256         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
257         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
258         printf("    ncacn_np:\n");
259         printf("    ncacn_np:[/pipe/samr]\n\n");
260
261         printf("    ncacn_ip_tcp:myserver\n");
262         printf("    ncacn_ip_tcp:myserver[1024]\n");
263         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
264
265         printf("    ncalrpc:\n\n");
266
267         printf("The UNC format is:\n\n");
268
269         printf("  //server/share\n\n");
270
271         printf("Tests are:");
272
273         if (torture_root == NULL) {
274             printf("NO TESTS LOADED\n");
275             exit(1);
276         }
277
278         for (o = torture_root->children; o; o = o->next) {
279                 printf("\n%s (%s):\n  ", o->description, o->name);
280
281                 i = 0;
282                 for (s = o->children; s; s = s->next) {
283                         if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
284                                 printf("\n  ");
285                                 i = 0;
286                         }
287                         i+=printf("%s-%s ", o->name, s->name);
288                 }
289
290                 for (t = o->testcases; t; t = t->next) {
291                         if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
292                                 printf("\n  ");
293                                 i = 0;
294                         }
295                         i+=printf("%s-%s ", o->name, t->name);
296                 }
297
298                 if (i) printf("\n");
299         }
300
301         printf("\nThe default test is ALL.\n");
302
303         exit(1);
304 }
305
306 _NORETURN_ static void max_runtime_handler(int sig)
307 {
308         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
309         exit(1);
310 }
311
312 struct timeval last_suite_started;
313
314 static void simple_suite_start(struct torture_context *ctx,
315                                struct torture_suite *suite)
316 {
317         last_suite_started = timeval_current();
318         printf("Running %s\n", suite->name);
319 }
320
321 static void simple_suite_finish(struct torture_context *ctx,
322                                 struct torture_suite *suite)
323 {
324
325         printf("%s took %g secs\n\n", suite->name, 
326                    timeval_elapsed(&last_suite_started));
327 }
328
329 static void simple_test_result(struct torture_context *context, 
330                                enum torture_result res, const char *reason)
331 {
332         switch (res) {
333         case TORTURE_OK:
334                 if (reason)
335                         printf("OK: %s\n", reason);
336                 break;
337         case TORTURE_FAIL:
338                 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
339                 break;
340         case TORTURE_ERROR:
341                 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason); 
342                 break;
343         case TORTURE_SKIP:
344                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
345                 break;
346         }
347 }
348
349 static void simple_comment(struct torture_context *test, 
350                            const char *comment)
351 {
352         printf("%s", comment);
353 }
354
355 static void simple_warning(struct torture_context *test, 
356                            const char *comment)
357 {
358         fprintf(stderr, "WARNING: %s\n", comment);
359 }
360
361 const static struct torture_ui_ops std_ui_ops = {
362         .comment = simple_comment,
363         .warning = simple_warning,
364         .suite_start = simple_suite_start,
365         .suite_finish = simple_suite_finish,
366         .test_result = simple_test_result
367 };
368
369
370 static void run_shell(struct torture_context *tctx)
371 {
372         char *cline;
373         int argc;
374         const char **argv;
375         int ret;
376
377         while (1) {
378                 cline = smb_readline("torture> ", NULL, NULL);
379
380                 if (cline == NULL)
381                         return;
382         
383                 ret = poptParseArgvString(cline, &argc, &argv);
384                 if (ret != 0) {
385                         fprintf(stderr, "Error parsing line\n");
386                         continue;
387                 }
388
389                 if (!strcmp(argv[0], "quit")) {
390                         return;
391                 } else if (!strcmp(argv[0], "set")) {
392                         if (argc < 3) {
393                                 fprintf(stderr, "Usage: set <variable> <value>\n");
394                         } else {
395                                 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
396                                 lp_set_cmdline(tctx->lp_ctx, name, argv[2]);
397                                 talloc_free(name);
398                         }
399                 } else if (!strcmp(argv[0], "help")) {
400                         fprintf(stderr, "Available commands:\n"
401                                                         " help - This help command\n"
402                                                         " run - Run test\n"
403                                                         " set - Change variables\n"
404                                                         "\n");
405                 } else if (!strcmp(argv[0], "run")) {
406                         if (argc < 2) {
407                                 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
408                         } else {
409                                 run_test(tctx, argv[1]);
410                         }
411                 }
412                 free(cline);
413         }
414 }
415
416 /****************************************************************************
417   main program
418 ****************************************************************************/
419 int main(int argc,char *argv[])
420 {
421         int opt, i;
422         bool correct = true;
423         int max_runtime=0;
424         int argc_new;
425         struct torture_context *torture;
426         struct torture_results *results;
427         const struct torture_ui_ops *ui_ops;
428         char **argv_new;
429         poptContext pc;
430         static const char *target = "other";
431         NTSTATUS status;
432         int shell = false;
433         static const char *ui_ops_name = "subunit";
434         const char *basedir = NULL;
435         const char *extra_module = NULL;
436         static int list_tests = 0;
437         int num_extra_users = 0;
438         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
439               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS,
440               OPT_EXTRA_USER,};
441
442         struct poptOption long_options[] = {
443                 POPT_AUTOHELP
444                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL },
445                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
446                 {"basedir",       0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" },
447                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "Seed to use for randomizer",   NULL},
448                 {"num-progs",     0, POPT_ARG_INT,  NULL,       OPT_NUMPROGS,   "num progs",    NULL},
449                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
450                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
451                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "NBench load file to use",      NULL},
452                 {"list",          0, POPT_ARG_NONE, &list_tests, 0, "List available tests and exit", NULL },
453                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
454                 {"timelimit",   't', POPT_ARG_INT,      NULL,   OPT_TIMELIMIT,  "Set time limit (in seconds)",  NULL},
455                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
456                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
457                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
458                  "run dangerous tests (eg. wiping out password database)", NULL},
459                 {"load-module",  0,  POPT_ARG_STRING, &extra_module,     0, "load tests from DSO file",    "SOFILE"},
460                 {"shell",               0, POPT_ARG_NONE, &shell, true, "Run shell", NULL},
461                 {"target",              'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
462                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
463                  "run async tests", NULL},
464                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
465                  "number of simultaneous async requests", NULL},
466                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
467                  "set maximum time for smbtorture to live", "seconds"},
468                 {"extra-user",   0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER,
469                  "extra user credentials", NULL},
470                 POPT_COMMON_SAMBA
471                 POPT_COMMON_CONNECTION
472                 POPT_COMMON_CREDENTIALS
473                 POPT_COMMON_VERSION
474                 { NULL }
475         };
476
477         setlinebuf(stdout);
478
479         /* we are never interested in SIGPIPE */
480         BlockSignals(true, SIGPIPE);
481
482         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
483                             POPT_CONTEXT_KEEP_FIRST);
484
485         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
486
487         while((opt = poptGetNextOpt(pc)) != -1) {
488                 switch (opt) {
489                 case OPT_LOADFILE:
490                         lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc));
491                         break;
492                 case OPT_UNCLIST:
493                         lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
494                         break;
495                 case OPT_TIMELIMIT:
496                         lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc));
497                         break;
498                 case OPT_NUMPROGS:
499                         lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc));
500                         break;
501                 case OPT_DNS:
502                         parse_dns(cmdline_lp_ctx, poptGetOptArg(pc));
503                         break;
504                 case OPT_DANGEROUS:
505                         lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes");
506                         break;
507                 case OPT_ASYNC:
508                         lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes");
509                         break;
510                 case OPT_SMB_PORTS:
511                         lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc));
512                         break;
513                 case OPT_EXTRA_USER:
514                         {
515                                 char *option = talloc_asprintf(NULL, "torture:extra_user%u",
516                                                                ++num_extra_users);
517                                 char *value = poptGetOptArg(pc);
518                                 lp_set_cmdline(cmdline_lp_ctx, option, value);
519                                 talloc_free(option);
520                         }
521                         break;
522                 default:
523                         printf("bad command line option\n");
524                         exit(1);
525                 }
526         }
527
528         if (strcmp(target, "samba3") == 0) {
529                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true");
530         } else if (strcmp(target, "samba4") == 0) {
531                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true");
532         } else if (strcmp(target, "w2k8") == 0) {
533                 lp_set_cmdline(cmdline_lp_ctx, "torture:w2k8", "true");
534         } else if (strcmp(target, "win7") == 0) {
535                 lp_set_cmdline(cmdline_lp_ctx, "torture:win7", "true");
536         } else if (strcmp(target, "onefs") == 0) {
537                 lp_set_cmdline(cmdline_lp_ctx, "torture:sacl_support", "false");
538         }
539
540         if (max_runtime) {
541                 /* this will only work if nobody else uses alarm(),
542                    which means it won't work for some tests, but we
543                    can't use the event context method we use for smbd
544                    as so many tests create their own event
545                    context. This will at least catch most cases. */
546                 signal(SIGALRM, max_runtime_handler);
547                 alarm(max_runtime);
548         }
549
550         if (extra_module != NULL) {
551             init_module_fn fn = load_module(talloc_autofree_context(), poptGetOptArg(pc));
552
553             if (fn == NULL) 
554                 d_printf("Unable to load module from %s\n", poptGetOptArg(pc));
555             else {
556                 status = fn();
557                 if (NT_STATUS_IS_ERR(status)) {
558                     d_printf("Error initializing module %s: %s\n", 
559                              poptGetOptArg(pc), nt_errstr(status));
560                 }
561             }
562         } else { 
563                 torture_init();
564         }
565
566         if (list_tests) {
567                 print_test_list();
568                 return 0;
569         }
570
571         if (torture_seed == 0) {
572                 torture_seed = time(NULL);
573         } 
574         printf("Using seed %d\n", torture_seed);
575         srandom(torture_seed);
576
577         argv_new = discard_const_p(char *, poptGetArgs(pc));
578
579         argc_new = argc;
580         for (i=0; i<argc; i++) {
581                 if (argv_new[i] == NULL) {
582                         argc_new = i;
583                         break;
584                 }
585         }
586
587         if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
588                 usage(pc);
589                 exit(1);
590         }
591
592         if (!parse_target(cmdline_lp_ctx, argv_new[1])) {
593                 usage(pc);
594                 exit(1);
595         }
596
597         if (!strcmp(ui_ops_name, "simple")) {
598                 ui_ops = &std_ui_ops;
599         } else if (!strcmp(ui_ops_name, "subunit")) {
600                 ui_ops = &torture_subunit_ui_ops;
601         } else {
602                 printf("Unknown output format '%s'\n", ui_ops_name);
603                 exit(1);
604         }
605
606         results = torture_results_init(talloc_autofree_context(), ui_ops);
607
608         torture = torture_context_init(s4_event_context_init(NULL), results);
609         if (basedir != NULL) {
610                 if (basedir[0] != '/') {
611                         fprintf(stderr, "Please specify an absolute path to --basedir\n");
612                         return 1;
613                 }
614                 torture->outputdir = basedir;
615         } else {
616                 char *pwd = talloc_size(torture, PATH_MAX);
617                 if (!getcwd(pwd, PATH_MAX)) {
618                         fprintf(stderr, "Unable to determine current working directory\n");
619                         return 1;
620                 }
621                 torture->outputdir = pwd;
622         }
623
624         torture->lp_ctx = cmdline_lp_ctx;
625
626         gensec_init(cmdline_lp_ctx);
627
628         if (argc_new == 0) {
629                 printf("You must specify a test to run, or 'ALL'\n");
630         } else if (shell) {
631                 run_shell(torture);
632         } else {
633                 for (i=2;i<argc_new;i++) {
634                         if (!run_test(torture, argv_new[i])) {
635                                 correct = false;
636                         }
637                 }
638         }
639
640         if (torture->results->returncode && correct) {
641                 return(0);
642         } else {
643                 return(1);
644         }
645 }