Patch from metze and me that adds dummy smb_register_*() functions
[samba.git] / source3 / torture / masktest.c
1 /* 
2    Unix SMB/CIFS implementation.
3    mask_match tester
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #define NO_SYSLOG
22
23 #include "includes.h"
24
25 #include "module_dummy.h"
26
27 static fstring password;
28 static fstring username;
29 static int got_pass;
30 static int max_protocol = PROTOCOL_NT1;
31 static BOOL showall = False;
32 static BOOL old_list = False;
33 static const char *maskchars = "<>\"?*abc.";
34 static const char *filechars = "abcdefghijklm.";
35 static int verbose;
36 static int die_on_error;
37 static int NumLoops = 0;
38 static int ignore_dot_errors = 0;
39
40 /* a test fn for LANMAN mask support */
41 int ms_fnmatch_lanman_core(const char *pattern, const char *string)
42 {
43         const char *p = pattern, *n = string;
44         char c;
45
46         if (strcmp(p,"?")==0 && strcmp(n,".")==0) goto match;
47
48         while ((c = *p++)) {
49                 switch (c) {
50                 case '.':
51                         /* if (! *n && ! *p) goto match; */
52                         if (*n != '.') goto nomatch;
53                         n++;
54                         break;
55
56                 case '?':
57                         if ((*n == '.' && n[1] != '.') || ! *n) goto next;
58                         n++;
59                         break;
60
61                 case '>':
62                         if (n[0] == '.') {
63                                 if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match;
64                                 if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
65                                 goto nomatch;
66                         }
67                         if (! *n) goto next;
68                         n++;
69                         break;
70
71                 case '*':
72                         if (! *p) goto match;
73                         for (; *n; n++) {
74                                 if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
75                         }
76                         break;
77
78                 case '<':
79                         for (; *n; n++) {
80                                 if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
81                                 if (*n == '.' && !strchr_m(n+1,'.')) {
82                                         n++;
83                                         break;
84                                 }
85                         }
86                         break;
87
88                 case '"':
89                         if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match;
90                         if (*n != '.') goto nomatch;
91                         n++;
92                         break;
93
94                 default:
95                         if (c != *n) goto nomatch;
96                         n++;
97                 }
98         }
99         
100         if (! *n) goto match;
101         
102  nomatch:
103         if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
104         return -1;
105
106 next:
107         if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
108         goto nomatch;
109
110  match:
111         if (verbose) printf("MATCH   pattern=[%s] string=[%s]\n", pattern, string);
112         return 0;
113 }
114
115 int ms_fnmatch_lanman(const char *pattern, const char *string)
116 {
117         if (!strpbrk(pattern, "?*<>\"")) {
118                 if (strcmp(string,"..") == 0) 
119                         string = ".";
120
121                 return strcmp(pattern, string);
122         }
123
124         if (strcmp(string,"..") == 0 || strcmp(string,".") == 0) {
125                 return ms_fnmatch_lanman_core(pattern, "..") &&
126                         ms_fnmatch_lanman_core(pattern, ".");
127         }
128
129         return ms_fnmatch_lanman_core(pattern, string);
130 }
131
132 static BOOL reg_match_one(struct cli_state *cli, const char *pattern, const char *file)
133 {
134         /* oh what a weird world this is */
135         if (old_list && strcmp(pattern, "*.*") == 0) return True;
136
137         if (strcmp(pattern,".") == 0) return False;
138
139         if (max_protocol <= PROTOCOL_LANMAN2) {
140                 return ms_fnmatch_lanman(pattern, file)==0;
141         }
142
143         if (strcmp(file,"..") == 0) file = ".";
144
145         return ms_fnmatch(pattern, file, cli->protocol)==0;
146 }
147
148 static char *reg_test(struct cli_state *cli, char *pattern, char *long_name, char *short_name)
149 {
150         static fstring ret;
151         fstrcpy(ret, "---");
152
153         pattern = 1+strrchr_m(pattern,'\\');
154
155         if (reg_match_one(cli, pattern, ".")) ret[0] = '+';
156         if (reg_match_one(cli, pattern, "..")) ret[1] = '+';
157         if (reg_match_one(cli, pattern, long_name) || 
158             (*short_name && reg_match_one(cli, pattern, short_name))) ret[2] = '+';
159         return ret;
160 }
161
162
163 /***************************************************** 
164 return a connection to a server
165 *******************************************************/
166 struct cli_state *connect_one(char *share)
167 {
168         struct cli_state *c;
169         struct nmb_name called, calling;
170         char *server_n;
171         char *server;
172         struct in_addr ip;
173
174         server = share+2;
175         share = strchr_m(server,'\\');
176         if (!share) return NULL;
177         *share = 0;
178         share++;
179
180         server_n = server;
181         
182         zero_ip(&ip);
183
184         make_nmb_name(&calling, "masktest", 0x0);
185         make_nmb_name(&called , server, 0x20);
186
187  again:
188         zero_ip(&ip);
189
190         /* have to open a new connection */
191         if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
192                 DEBUG(0,("Connection to %s failed\n", server_n));
193                 return NULL;
194         }
195
196         c->protocol = max_protocol;
197
198         if (!cli_session_request(c, &calling, &called)) {
199                 DEBUG(0,("session request to %s failed\n", called.name));
200                 cli_shutdown(c);
201                 if (strcmp(called.name, "*SMBSERVER")) {
202                         make_nmb_name(&called , "*SMBSERVER", 0x20);
203                         goto again;
204                 }
205                 return NULL;
206         }
207
208         DEBUG(4,(" session request ok\n"));
209
210         if (!cli_negprot(c)) {
211                 DEBUG(0,("protocol negotiation failed\n"));
212                 cli_shutdown(c);
213                 return NULL;
214         }
215
216         if (!got_pass) {
217                 char *pass = getpass("Password: ");
218                 if (pass) {
219                         fstrcpy(password, pass);
220                 }
221         }
222
223         if (!cli_session_setup(c, username, 
224                                password, strlen(password),
225                                password, strlen(password),
226                                lp_workgroup())) {
227                 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
228                 return NULL;
229         }
230
231         /*
232          * These next two lines are needed to emulate
233          * old client behaviour for people who have
234          * scripts based on client output.
235          * QUESTION ? Do we want to have a 'client compatibility
236          * mode to turn these on/off ? JRA.
237          */
238
239         if (*c->server_domain || *c->server_os || *c->server_type)
240                 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
241                         c->server_domain,c->server_os,c->server_type));
242         
243         DEBUG(4,(" session setup ok\n"));
244
245         if (!cli_send_tconX(c, share, "?????",
246                             password, strlen(password)+1)) {
247                 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
248                 cli_shutdown(c);
249                 return NULL;
250         }
251
252         DEBUG(4,(" tconx ok\n"));
253
254         return c;
255 }
256
257 static char *resultp;
258 static file_info *f_info;
259
260 static void listfn(file_info *f, const char *s, void *state)
261 {
262         if (strcmp(f->name,".") == 0) {
263                 resultp[0] = '+';
264         } else if (strcmp(f->name,"..") == 0) {
265                 resultp[1] = '+';               
266         } else {
267                 resultp[2] = '+';
268         }
269         f_info = f;
270 }
271
272 static void get_real_name(struct cli_state *cli, 
273                           pstring long_name, fstring short_name)
274 {
275         /* nasty hack to force level 260 listings - tridge */
276         cli->capabilities |= CAP_NT_SMBS;
277         if (max_protocol <= PROTOCOL_LANMAN1) {
278                 cli_list_new(cli, "\\masktest\\*.*", aHIDDEN | aDIR, listfn, NULL);
279         } else {
280                 cli_list_new(cli, "\\masktest\\*", aHIDDEN | aDIR, listfn, NULL);
281         }
282         if (f_info) {
283                 fstrcpy(short_name, f_info->short_name);
284                 strlower(short_name);
285                 pstrcpy(long_name, f_info->name);
286                 strlower(long_name);
287         }
288
289         if (*short_name == 0) {
290                 fstrcpy(short_name, long_name);
291         }
292
293 #if 0
294         if (!strchr_m(short_name,'.')) {
295                 fstrcat(short_name,".");
296         }
297 #endif
298 }
299
300 static void testpair(struct cli_state *cli, char *mask, char *file)
301 {
302         int fnum;
303         fstring res1;
304         char *res2;
305         static int count;
306         fstring short_name;
307         pstring long_name;
308
309         count++;
310
311         fstrcpy(res1, "---");
312
313         fnum = cli_open(cli, file, O_CREAT|O_TRUNC|O_RDWR, 0);
314         if (fnum == -1) {
315                 DEBUG(0,("Can't create %s\n", file));
316                 return;
317         }
318         cli_close(cli, fnum);
319
320         resultp = res1;
321         fstrcpy(short_name, "");
322         f_info = NULL;
323         get_real_name(cli, long_name, short_name);
324         f_info = NULL;
325         fstrcpy(res1, "---");
326         cli_list(cli, mask, aHIDDEN | aDIR, listfn, NULL);
327
328         res2 = reg_test(cli, mask, long_name, short_name);
329
330         if (showall || 
331             ((strcmp(res1, res2) && !ignore_dot_errors) ||
332              (strcmp(res1+2, res2+2) && ignore_dot_errors))) {
333                 DEBUG(0,("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n",
334                          res1, res2, count, mask, file, long_name, short_name));
335                 if (die_on_error) exit(1);
336         }
337
338         cli_unlink(cli, file);
339
340         if (count % 100 == 0) DEBUG(0,("%d\n", count));
341 }
342
343 static void test_mask(int argc, char *argv[], 
344                       struct cli_state *cli)
345 {
346         pstring mask, file;
347         int l1, l2, i, l;
348         int mc_len = strlen(maskchars);
349         int fc_len = strlen(filechars);
350
351         cli_mkdir(cli, "\\masktest");
352
353         cli_unlink(cli, "\\masktest\\*");
354
355         if (argc >= 2) {
356                 while (argc >= 2) {
357                         pstrcpy(mask,"\\masktest\\");
358                         pstrcpy(file,"\\masktest\\");
359                         pstrcat(mask, argv[0]);
360                         pstrcat(file, argv[1]);
361                         testpair(cli, mask, file);
362                         argv += 2;
363                         argc -= 2;
364                 }
365                 goto finished;
366         }
367
368         while (1) {
369                 l1 = 1 + random() % 20;
370                 l2 = 1 + random() % 20;
371                 pstrcpy(mask,"\\masktest\\");
372                 pstrcpy(file,"\\masktest\\");
373                 l = strlen(mask);
374                 for (i=0;i<l1;i++) {
375                         mask[i+l] = maskchars[random() % mc_len];
376                 }
377                 mask[l+l1] = 0;
378
379                 for (i=0;i<l2;i++) {
380                         file[i+l] = filechars[random() % fc_len];
381                 }
382                 file[l+l2] = 0;
383
384                 if (strcmp(file+l,".") == 0 || 
385                     strcmp(file+l,"..") == 0 ||
386                     strcmp(mask+l,"..") == 0) continue;
387
388                 if (strspn(file+l, ".") == strlen(file+l)) continue;
389
390                 testpair(cli, mask, file);
391                 if (NumLoops && (--NumLoops == 0))
392                         break;
393         }
394
395  finished:
396         cli_rmdir(cli, "\\masktest");
397 }
398
399
400 static void usage(void)
401 {
402         printf(
403 "Usage:\n\
404   masktest //server/share [options..]\n\
405   options:\n\
406         -d debuglevel\n\
407         -n numloops\n\
408         -W workgroup\n\
409         -U user%%pass\n\
410         -s seed\n\
411         -M max protocol\n\
412         -f filechars (default %s)\n\
413         -m maskchars (default %s)\n\
414         -v                             verbose mode\n\
415         -E                             die on error\n\
416         -a                             show all tests\n\
417         -i                             ignore . and .. errors\n\
418 \n\
419   This program tests wildcard matching between two servers. It generates\n\
420   random pairs of filenames/masks and tests that they match in the same\n\
421   way on the servers and internally\n\
422 ", 
423   filechars, maskchars);
424 }
425
426 /****************************************************************************
427   main program
428 ****************************************************************************/
429  int main(int argc,char *argv[])
430 {
431         char *share;
432         struct cli_state *cli;  
433         extern char *optarg;
434         extern int optind;
435         extern BOOL AllowDebugChange;
436         int opt;
437         char *p;
438         int seed;
439
440         setlinebuf(stdout);
441
442         dbf = x_stderr;
443
444         DEBUGLEVEL = 0;
445         AllowDebugChange = False;
446
447         if (argc < 2 || argv[1][0] == '-') {
448                 usage();
449                 exit(1);
450         }
451
452         share = argv[1];
453
454         all_string_sub(share,"/","\\",0);
455
456         setup_logging(argv[0],True);
457
458         argc -= 1;
459         argv += 1;
460
461         lp_load(dyn_CONFIGFILE,True,False,False);
462         load_interfaces();
463
464         if (getenv("USER")) {
465                 fstrcpy(username,getenv("USER"));
466         }
467
468         seed = time(NULL);
469
470         while ((opt = getopt(argc, argv, "n:d:U:s:hm:f:aoW:M:vEi")) != EOF) {
471                 switch (opt) {
472                 case 'n':
473                         NumLoops = atoi(optarg);
474                         break;
475                 case 'd':
476                         DEBUGLEVEL = atoi(optarg);
477                         break;
478                 case 'E':
479                         die_on_error = 1;
480                         break;
481                 case 'i':
482                         ignore_dot_errors = 1;
483                         break;
484                 case 'v':
485                         verbose++;
486                         break;
487                 case 'M':
488                         max_protocol = interpret_protocol(optarg, max_protocol);
489                         break;
490                 case 'U':
491                         fstrcpy(username,optarg);
492                         p = strchr_m(username,'%');
493                         if (p) {
494                                 *p = 0;
495                                 fstrcpy(password, p+1);
496                                 got_pass = 1;
497                         }
498                         break;
499                 case 's':
500                         seed = atoi(optarg);
501                         break;
502                 case 'h':
503                         usage();
504                         exit(1);
505                 case 'm':
506                         maskchars = optarg;
507                         break;
508                 case 'f':
509                         filechars = optarg;
510                         break;
511                 case 'a':
512                         showall = 1;
513                         break;
514                 case 'o':
515                         old_list = True;
516                         break;
517                 default:
518                         printf("Unknown option %c (%d)\n", (char)opt, opt);
519                         exit(1);
520                 }
521         }
522
523         argc -= optind;
524         argv += optind;
525
526
527         cli = connect_one(share);
528         if (!cli) {
529                 DEBUG(0,("Failed to connect to %s\n", share));
530                 exit(1);
531         }
532
533         /* need to init seed after connect as clientgen uses random numbers */
534         DEBUG(0,("seed=%d\n", seed));
535         srandom(seed);
536
537         test_mask(argc, argv, cli);
538
539         return(0);
540 }