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 3 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, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21#include "lib/cmdline/popt_common.h" 22#include "system/filesys.h" 23#include "system/dir.h" 24#include "libcli/libcli.h" 25#include "libcli/raw/libcliraw.h" 26#include "system/time.h" 27#include "auth/credentials/credentials.h" 28#include "auth/gensec/gensec.h" 29#include "param/param.h" 30#include "dynconfig/dynconfig.h" 31#include "libcli/resolve/resolve.h" 32#include "lib/events/events.h" 33 34static bool showall = false; 35static bool old_list = false; 36static const char *maskchars = "<>\"?*abc."; 37static const char *filechars = "abcdefghijklm."; 38static int die_on_error; 39static int NumLoops = 0; 40static int max_length = 20; 41struct masktest_state { 42 TALLOC_CTX *mem_ctx; 43}; 44 45static bool reg_match_one(struct smbcli_state *cli, const char *pattern, const char *file) 46{ 47 /* oh what a weird world this is */ 48 if (old_list && strcmp(pattern, "*.*") == 0) return true; 49 50 if (ISDOT(pattern)) return false; 51 52 if (ISDOTDOT(file)) file = "."; 53 54 return ms_fnmatch(pattern, file, cli->transport->negotiate.protocol)==0; 55} 56 57static char *reg_test(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *pattern, const char *long_name, const char *short_name) 58{ 59 char *ret; 60 ret = talloc_strdup(mem_ctx, "---"); 61 62 pattern = 1+strrchr_m(pattern,'\\'); 63 64 if (reg_match_one(cli, pattern, ".")) ret[0] = '+'; 65 if (reg_match_one(cli, pattern, "..")) ret[1] = '+'; 66 if (reg_match_one(cli, pattern, long_name) || 67 (*short_name && reg_match_one(cli, pattern, short_name))) ret[2] = '+'; 68 return ret; 69} 70 71 72/***************************************************** 73return a connection to a server 74*******************************************************/ 75static struct smbcli_state *connect_one(struct resolve_context *resolve_ctx, 76 struct tevent_context *ev, 77 TALLOC_CTX *mem_ctx, 78 char *share, const char **ports, 79 const char *socket_options, 80 struct smbcli_options *options, 81 struct smbcli_session_options *session_options, 82 struct smb_iconv_convenience *iconv_convenience, 83 struct gensec_settings *gensec_settings) 84{ 85 struct smbcli_state *c; 86 char *server; 87 NTSTATUS status; 88 89 server = talloc_strdup(mem_ctx, share+2); 90 share = strchr_m(server,'\\'); 91 if (!share) return NULL; 92 *share = 0; 93 share++; 94 95 cli_credentials_set_workstation(cmdline_credentials, "masktest", CRED_SPECIFIED); 96 97 status = smbcli_full_connection(NULL, &c, 98 server, 99 ports, 100 share, NULL, 101 socket_options, 102 cmdline_credentials, resolve_ctx, ev, 103 options, session_options, 104 iconv_convenience, 105 gensec_settings); 106 107 if (!NT_STATUS_IS_OK(status)) { 108 return NULL; 109 } 110 111 return c; 112} 113 114static char *resultp; 115static struct { 116 char *long_name; 117 char *short_name; 118} last_hit; 119static bool f_info_hit; 120 121static void listfn(struct clilist_file_info *f, const char *s, void *state) 122{ 123 struct masktest_state *m = (struct masktest_state *)state; 124 125 if (ISDOT(f->name)) { 126 resultp[0] = '+'; 127 } else if (ISDOTDOT(f->name)) { 128 resultp[1] = '+'; 129 } else { 130 resultp[2] = '+'; 131 } 132 133 last_hit.long_name = talloc_strdup(m->mem_ctx, f->name); 134 last_hit.short_name = talloc_strdup(m->mem_ctx, f->short_name); 135 f_info_hit = true; 136} 137 138static void get_real_name(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, 139 char **long_name, char **short_name) 140{ 141 const char *mask; 142 struct masktest_state state; 143 144 if (cli->transport->negotiate.protocol <= PROTOCOL_LANMAN1) { 145 mask = "\\masktest\\*.*"; 146 } else { 147 mask = "\\masktest\\*"; 148 } 149 150 f_info_hit = false; 151 152 state.mem_ctx = mem_ctx; 153 154 smbcli_list_new(cli->tree, mask, 155 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, 156 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, 157 listfn, &state); 158 159 if (f_info_hit) { 160 *short_name = talloc_strdup(mem_ctx, last_hit.short_name); 161 strlower(*short_name); 162 *long_name = talloc_strdup(mem_ctx, last_hit.long_name); 163 strlower(*long_name); 164 } 165 166 if (*short_name == '\0') { 167 *short_name = talloc_strdup(mem_ctx, *long_name); 168 } 169} 170 171static void testpair(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, char *mask, 172 char *file) 173{ 174 int fnum; 175 char res1[256]; 176 char *res2; 177 static int count; 178 char *short_name = NULL; 179 char *long_name = NULL; 180 struct masktest_state state; 181 182 count++; 183 184 safe_strcpy(res1, "---", sizeof(res1)); 185 186 state.mem_ctx = mem_ctx; 187 188 fnum = smbcli_open(cli->tree, file, O_CREAT|O_TRUNC|O_RDWR, 0); 189 if (fnum == -1) { 190 DEBUG(0,("Can't create %s\n", file)); 191 return; 192 } 193 smbcli_close(cli->tree, fnum); 194 195 resultp = res1; 196 short_name = talloc_strdup(mem_ctx, ""); 197 get_real_name(mem_ctx, cli, &long_name, &short_name); 198 safe_strcpy(res1, "---", sizeof(res1)); 199 smbcli_list_new(cli->tree, mask, 200 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, 201 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, 202 listfn, &state); 203 204 res2 = reg_test(cli, mem_ctx, mask, long_name, short_name); 205 206 if (showall || strcmp(res1, res2)) { 207 d_printf("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n", 208 res1, res2, count, mask, file, long_name, short_name); 209 if (die_on_error) exit(1); 210 } 211 212 smbcli_unlink(cli->tree, file); 213 214 if (count % 100 == 0) DEBUG(0,("%d\n", count)); 215 216 resultp = NULL; 217} 218 219static void test_mask(int argc, char *argv[], 220 TALLOC_CTX *mem_ctx, 221 struct smbcli_state *cli) 222{ 223 char *mask, *file; 224 int l1, l2, i, l; 225 int mc_len = strlen(maskchars); 226 int fc_len = strlen(filechars); 227 228 smbcli_mkdir(cli->tree, "\\masktest"); 229 230 smbcli_unlink(cli->tree, "\\masktest\\*"); 231 232 if (argc >= 2) { 233 while (argc >= 2) { 234 mask = talloc_strdup(mem_ctx, "\\masktest\\"); 235 file = talloc_strdup(mem_ctx, "\\masktest\\"); 236 mask = talloc_strdup_append(mask, argv[0]); 237 file = talloc_strdup_append(file, argv[1]); 238 testpair(mem_ctx, cli, mask, file); 239 argv += 2; 240 argc -= 2; 241 } 242 goto finished; 243 } 244 245 while (1) { 246 l1 = 1 + random() % max_length; 247 l2 = 1 + random() % max_length; 248 mask = talloc_strdup(mem_ctx, "\\masktest\\"); 249 file = talloc_strdup(mem_ctx, "\\masktest\\"); 250 mask = talloc_realloc_size(mem_ctx, mask, strlen(mask)+l1+1); 251 file = talloc_realloc_size(mem_ctx, file, strlen(file)+l2+1); 252 l = strlen(mask); 253 for (i=0;i<l1;i++) { 254 mask[i+l] = maskchars[random() % mc_len]; 255 } 256 mask[l+l1] = 0; 257 258 for (i=0;i<l2;i++) { 259 file[i+l] = filechars[random() % fc_len]; 260 } 261 file[l+l2] = 0; 262 263 if (ISDOT(file+l) || ISDOTDOT(file+l) || ISDOTDOT(mask+l)) { 264 continue; 265 } 266 267 if (strspn(file+l, ".") == strlen(file+l)) continue; 268 269 testpair(mem_ctx, cli, mask, file); 270 if (NumLoops && (--NumLoops == 0)) 271 break; 272 } 273 274 finished: 275 smbcli_rmdir(cli->tree, "\\masktest"); 276 talloc_free(mem_ctx); 277} 278 279 280static void usage(poptContext pc) 281{ 282 printf( 283"Usage:\n\ 284 masktest //server/share [options..]\n\ 285\n\ 286 This program tests wildcard matching between two servers. It generates\n\ 287 random pairs of filenames/masks and tests that they match in the same\n\ 288 way on the servers and internally\n"); 289 poptPrintUsage(pc, stdout, 0); 290} 291 292/**************************************************************************** 293 main program 294****************************************************************************/ 295 int main(int argc,char *argv[]) 296{ 297 char *share; 298 struct smbcli_state *cli; 299 int opt; 300 int seed; 301 struct tevent_context *ev; 302 struct loadparm_context *lp_ctx; 303 struct smbcli_options options; 304 struct smbcli_session_options session_options; 305 poptContext pc; 306 int argc_new, i; 307 char **argv_new; 308 TALLOC_CTX *mem_ctx; 309 enum {OPT_UNCLIST=1000}; 310 struct poptOption long_options[] = { 311 POPT_AUTOHELP 312 {"seed", 0, POPT_ARG_INT, &seed, 0, "Seed to use for randomizer", NULL}, 313 {"num-ops", 0, POPT_ARG_INT, &NumLoops, 0, "num ops", NULL}, 314 {"maxlength", 0, POPT_ARG_INT, &max_length,0, "maximum length", NULL}, 315 {"dieonerror", 0, POPT_ARG_NONE, &die_on_error, 0, "die on errors", NULL}, 316 {"showall", 0, POPT_ARG_NONE, &showall, 0, "display all operations", NULL}, 317 {"oldlist", 0, POPT_ARG_NONE, &old_list, 0, "use old list call", NULL}, 318 {"maskchars", 0, POPT_ARG_STRING, &maskchars, 0,"mask characters", NULL}, 319 {"filechars", 0, POPT_ARG_STRING, &filechars, 0,"file characters", NULL}, 320 POPT_COMMON_SAMBA 321 POPT_COMMON_CONNECTION 322 POPT_COMMON_CREDENTIALS 323 POPT_COMMON_VERSION 324 { NULL } 325 }; 326 327 setlinebuf(stdout); 328 seed = time(NULL); 329 330 pc = poptGetContext("locktest", argc, (const char **) argv, long_options, 331 POPT_CONTEXT_KEEP_FIRST); 332 333 poptSetOtherOptionHelp(pc, "<unc>"); 334 335 while((opt = poptGetNextOpt(pc)) != -1) { 336 switch (opt) { 337 case OPT_UNCLIST: 338 lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc)); 339 break; 340 } 341 } 342 343 argv_new = discard_const_p(char *, poptGetArgs(pc)); 344 argc_new = argc; 345 for (i=0; i<argc; i++) { 346 if (argv_new[i] == NULL) { 347 argc_new = i; 348 break; 349 } 350 } 351 352 if (!(argc_new >= 2)) { 353 usage(pc); 354 exit(1); 355 } 356 357 setup_logging("masktest", DEBUG_STDOUT); 358 359 share = argv_new[1]; 360 361 all_string_sub(share,"/","\\",0); 362 363 lp_ctx = cmdline_lp_ctx; 364 365 mem_ctx = talloc_autofree_context(); 366 367 ev = s4_event_context_init(mem_ctx); 368 369 gensec_init(lp_ctx); 370 371 lp_smbcli_options(lp_ctx, &options); 372 lp_smbcli_session_options(lp_ctx, &session_options); 373 374 cli = connect_one(lp_resolve_context(lp_ctx), ev, mem_ctx, share, 375 lp_smb_ports(lp_ctx), lp_socket_options(lp_ctx), 376 &options, &session_options, 377 lp_iconv_convenience(lp_ctx), 378 lp_gensec_settings(mem_ctx, lp_ctx)); 379 if (!cli) { 380 DEBUG(0,("Failed to connect to %s\n", share)); 381 exit(1); 382 } 383 384 /* need to init seed after connect as clientgen uses random numbers */ 385 DEBUG(0,("seed=%d format --- --- (server, correct)\n", seed)); 386 srandom(seed); 387 388 test_mask(argc_new-1, argv_new+1, mem_ctx, cli); 389 390 return(0); 391} 392