s3: Use data_blob_null
[bbaumbach/samba-autobuild/.git] / source3 / libsmb / clilist.c
1 /*
2    Unix SMB/CIFS implementation.
3    client directory list routines
4    Copyright (C) Andrew Tridgell 1994-1998
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
22 /****************************************************************************
23  Calculate a safe next_entry_offset.
24 ****************************************************************************/
25
26 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
27 {
28         size_t next_entry_offset = (size_t)IVAL(base,0);
29
30         if (next_entry_offset == 0 ||
31                         base + next_entry_offset < base ||
32                         base + next_entry_offset > pdata_end) {
33                 next_entry_offset = pdata_end - base;
34         }
35         return next_entry_offset;
36 }
37
38 /****************************************************************************
39  Interpret a long filename structure - this is mostly guesses at the moment.
40  The length of the structure is returned
41  The structure of a long filename depends on the info level.
42  SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
43  by NT and SMB_FIND_EA_SIZE is used by OS/2
44 ****************************************************************************/
45
46 static size_t interpret_long_filename(TALLOC_CTX *ctx,
47                                         struct cli_state *cli,
48                                         int level,
49                                         const char *p,
50                                         const char *pdata_end,
51                                         struct file_info *finfo,
52                                         uint32 *p_resume_key,
53                                         DATA_BLOB *p_last_name_raw)
54 {
55         int len;
56         size_t ret;
57         const char *base = p;
58
59         data_blob_free(p_last_name_raw);
60
61         if (p_resume_key) {
62                 *p_resume_key = 0;
63         }
64         ZERO_STRUCTP(finfo);
65
66         switch (level) {
67                 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
68                         /* these dates are converted to GMT by
69                            make_unix_date */
70                         if (pdata_end - base < 27) {
71                                 return pdata_end - base;
72                         }
73                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
74                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
75                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
76                         finfo->size = IVAL(p,16);
77                         finfo->mode = CVAL(p,24);
78                         len = CVAL(p, 26);
79                         p += 27;
80                         p += clistr_align_in(cli, p, 0);
81
82                         /* We can safely use len here (which is required by OS/2)
83                          * and the NAS-BASIC server instead of +2 or +1 as the
84                          * STR_TERMINATE flag below is
85                          * actually used as the length calculation.
86                          * The len is merely an upper bound.
87                          * Due to the explicit 2 byte null termination
88                          * in cli_receive_trans/cli_receive_nt_trans
89                          * we know this is safe. JRA + kukks
90                          */
91
92                         if (p + len > pdata_end) {
93                                 return pdata_end - base;
94                         }
95
96                         /* the len+2 below looks strange but it is
97                            important to cope with the differences
98                            between win2000 and win9x for this call
99                            (tridge) */
100                         ret = clistr_pull_talloc(ctx,
101                                                 cli->inbuf,
102                                                 &finfo->name,
103                                                 p,
104                                                 len+2,
105                                                 STR_TERMINATE);
106                         if (ret == (size_t)-1) {
107                                 return pdata_end - base;
108                         }
109                         p += ret;
110                         return PTR_DIFF(p, base);
111
112                 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
113                         /* these dates are converted to GMT by
114                            make_unix_date */
115                         if (pdata_end - base < 31) {
116                                 return pdata_end - base;
117                         }
118                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
119                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
120                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
121                         finfo->size = IVAL(p,16);
122                         finfo->mode = CVAL(p,24);
123                         len = CVAL(p, 30);
124                         p += 31;
125                         /* check for unisys! */
126                         if (p + len + 1 > pdata_end) {
127                                 return pdata_end - base;
128                         }
129                         ret = clistr_pull_talloc(ctx,
130                                                 cli->inbuf,
131                                                 &finfo->name,
132                                                 p,
133                                                 len,
134                                                 STR_NOALIGN);
135                         if (ret == (size_t)-1) {
136                                 return pdata_end - base;
137                         }
138                         p += ret;
139                         return PTR_DIFF(p, base) + 1;
140
141                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
142                 {
143                         size_t namelen, slen;
144
145                         if (pdata_end - base < 94) {
146                                 return pdata_end - base;
147                         }
148
149                         p += 4; /* next entry offset */
150
151                         if (p_resume_key) {
152                                 *p_resume_key = IVAL(p,0);
153                         }
154                         p += 4; /* fileindex */
155
156                         /* Offset zero is "create time", not "change time". */
157                         p += 8;
158                         finfo->atime_ts = interpret_long_date(p);
159                         p += 8;
160                         finfo->mtime_ts = interpret_long_date(p);
161                         p += 8;
162                         finfo->ctime_ts = interpret_long_date(p);
163                         p += 8;
164                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
165                         p += 8;
166                         p += 8; /* alloc size */
167                         finfo->mode = CVAL(p,0);
168                         p += 4;
169                         namelen = IVAL(p,0);
170                         p += 4;
171                         p += 4; /* EA size */
172                         slen = SVAL(p, 0);
173                         if (slen > 24) {
174                                 /* Bad short name length. */
175                                 return pdata_end - base;
176                         }
177                         p += 2;
178                         {
179                                 /* stupid NT bugs. grr */
180                                 int flags = 0;
181                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
182                                 clistr_pull(cli->inbuf, finfo->short_name, p,
183                                             sizeof(finfo->short_name),
184                                             slen, flags);
185                         }
186                         p += 24; /* short name? */
187                         if (p + namelen < p || p + namelen > pdata_end) {
188                                 return pdata_end - base;
189                         }
190                         ret = clistr_pull_talloc(ctx,
191                                                 cli->inbuf,
192                                                 &finfo->name,
193                                                 p,
194                                                 namelen,
195                                                 0);
196                         if (ret == (size_t)-1) {
197                                 return pdata_end - base;
198                         }
199
200                         /* To be robust in the face of unicode conversion failures
201                            we need to copy the raw bytes of the last name seen here.
202                            Namelen doesn't include the terminating unicode null, so
203                            copy it here. */
204
205                         if (p_last_name_raw) {
206                                 *p_last_name_raw = data_blob(NULL, namelen+2);
207                                 memcpy(p_last_name_raw->data, p, namelen);
208                                 SSVAL(p_last_name_raw->data, namelen, 0);
209                         }
210                         return calc_next_entry_offset(base, pdata_end);
211                 }
212         }
213
214         DEBUG(1,("Unknown long filename format %d\n",level));
215         return calc_next_entry_offset(base, pdata_end);
216 }
217
218 /****************************************************************************
219  Do a directory listing, calling fn on each file found.
220 ****************************************************************************/
221
222 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
223                  void (*fn)(const char *, struct file_info *, const char *,
224                             void *), void *state)
225 {
226 #if 1
227         int max_matches = 1366; /* Match W2k - was 512. */
228 #else
229         int max_matches = 512;
230 #endif
231         int info_level;
232         char *p, *p2, *rdata_end;
233         char *mask = NULL;
234         struct file_info finfo;
235         int i;
236         char *dirlist = NULL;
237         int dirlist_len = 0;
238         int total_received = -1;
239         bool First = True;
240         int ff_searchcount=0;
241         int ff_eos=0;
242         int ff_dir_handle=0;
243         int loop_count = 0;
244         char *rparam=NULL, *rdata=NULL;
245         unsigned int param_len, data_len;
246         uint16 setup;
247         char *param;
248         uint32 resume_key = 0;
249         TALLOC_CTX *frame = talloc_stackframe();
250         DATA_BLOB last_name_raw = data_blob_null;
251
252         /* NT uses SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
253            OS/2 uses SMB_FIND_EA_SIZE. Both accept SMB_FIND_INFO_STANDARD. */
254         info_level = (cli->capabilities&CAP_NT_SMBS)?
255                 SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
256
257         mask = SMB_STRDUP(Mask);
258         if (!mask) {
259                 TALLOC_FREE(frame);
260                 return -1;
261         }
262
263         ZERO_STRUCT(finfo);
264
265         while (ff_eos == 0) {
266                 size_t nlen = 2*(strlen(mask)+1);
267
268                 loop_count++;
269                 if (loop_count > 200) {
270                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
271                         break;
272                 }
273
274                 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
275                 if (!param) {
276                         break;
277                 }
278
279                 if (First) {
280                         setup = TRANSACT2_FINDFIRST;
281                         SSVAL(param,0,attribute); /* attribute */
282                         SSVAL(param,2,max_matches); /* max count */
283                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
284                         SSVAL(param,6,info_level);
285                         SIVAL(param,8,0);
286                         p = param+12;
287                         p += clistr_push(cli, param+12, mask,
288                                          nlen, STR_TERMINATE);
289                 } else {
290                         setup = TRANSACT2_FINDNEXT;
291                         SSVAL(param,0,ff_dir_handle);
292                         SSVAL(param,2,max_matches); /* max count */
293                         SSVAL(param,4,info_level);
294                         /* For W2K servers serving out FAT filesystems we *must* set the
295                            resume key. If it's not FAT then it's returned as zero. */
296                         SIVAL(param,6,resume_key); /* ff_resume_key */
297                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
298                            can miss filenames. Use last filename continue instead. JRA */
299                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
300                         p = param+12;
301                         if (last_name_raw.length) {
302                                 memcpy(p, last_name_raw.data, last_name_raw.length);
303                                 p += last_name_raw.length;
304                         } else {
305                                 p += clistr_push(cli, param+12, mask,
306                                                 nlen, STR_TERMINATE);
307                         }
308                 }
309
310                 param_len = PTR_DIFF(p, param);
311
312                 if (!cli_send_trans(cli, SMBtrans2,
313                                     NULL,                   /* Name */
314                                     -1, 0,                  /* fid, flags */
315                                     &setup, 1, 0,           /* setup, length, max */
316                                     param, param_len, 10,   /* param, length, max */
317                                     NULL, 0,
318 #if 0
319                                     /* w2k value. */
320                                     MIN(16384,cli->max_xmit) /* data, length, max. */
321 #else
322                                     cli->max_xmit           /* data, length, max. */
323 #endif
324                                     )) {
325                         SAFE_FREE(param);
326                         TALLOC_FREE(frame);
327                         break;
328                 }
329
330                 SAFE_FREE(param);
331
332                 if (!cli_receive_trans(cli, SMBtrans2,
333                                        &rparam, &param_len,
334                                        &rdata, &data_len) &&
335                     cli_is_dos_error(cli)) {
336                         /* We need to work around a Win95 bug - sometimes
337                            it gives ERRSRV/ERRerror temprarily */
338                         uint8 eclass;
339                         uint32 ecode;
340
341                         SAFE_FREE(rdata);
342                         SAFE_FREE(rparam);
343
344                         cli_dos_error(cli, &eclass, &ecode);
345
346                         /*
347                          * OS/2 might return "no more files",
348                          * which just tells us, that searchcount is zero
349                          * in this search.
350                          * Guenter Kukkukk <linux@kukkukk.com>
351                          */
352
353                         if (eclass == ERRDOS && ecode == ERRnofiles) {
354                                 ff_searchcount = 0;
355                                 cli_reset_error(cli);
356                                 break;
357                         }
358
359                         if (eclass != ERRSRV || ecode != ERRerror)
360                                 break;
361                         smb_msleep(100);
362                         continue;
363                 }
364
365                 if (cli_is_error(cli) || !rdata || !rparam) {
366                         SAFE_FREE(rdata);
367                         SAFE_FREE(rparam);
368                         break;
369                 }
370
371                 if (total_received == -1)
372                         total_received = 0;
373
374                 /* parse out some important return info */
375                 p = rparam;
376                 if (First) {
377                         ff_dir_handle = SVAL(p,0);
378                         ff_searchcount = SVAL(p,2);
379                         ff_eos = SVAL(p,4);
380                 } else {
381                         ff_searchcount = SVAL(p,0);
382                         ff_eos = SVAL(p,2);
383                 }
384
385                 if (ff_searchcount == 0) {
386                         SAFE_FREE(rdata);
387                         SAFE_FREE(rparam);
388                         break;
389                 }
390
391                 /* point to the data bytes */
392                 p = rdata;
393                 rdata_end = rdata + data_len;
394
395                 /* we might need the lastname for continuations */
396                 for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
397                         if ((info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) &&
398                                         (i == ff_searchcount-1)) {
399                                 /* Last entry - fixup the last offset length. */
400                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
401                         }
402                         p2 += interpret_long_filename(frame,
403                                                         cli,
404                                                         info_level,
405                                                         p2,
406                                                         rdata_end,
407                                                         &finfo,
408                                                         &resume_key,
409                                                         &last_name_raw);
410
411                         if (!finfo.name) {
412                                 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
413                                         info_level));
414                                 ff_eos = 1;
415                                 break;
416                         }
417                         if (!First && *mask && strcsequal(finfo.name, mask)) {
418                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
419                                         finfo.name));
420                                 ff_eos = 1;
421                                 break;
422                         }
423                 }
424
425                 SAFE_FREE(mask);
426                 if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
427                         mask = SMB_STRDUP(finfo.name);
428                 } else {
429                         mask = SMB_STRDUP("");
430                 }
431                 if (!mask) {
432                         SAFE_FREE(rdata);
433                         SAFE_FREE(rparam);
434                         break;
435                 }
436
437                 /* grab the data for later use */
438                 /* and add them to the dirlist pool */
439                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
440
441                 if (!dirlist) {
442                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
443                         SAFE_FREE(rdata);
444                         SAFE_FREE(rparam);
445                         break;
446                 }
447
448                 memcpy(dirlist+dirlist_len,p,data_len);
449                 dirlist_len += data_len;
450
451                 total_received += ff_searchcount;
452
453                 SAFE_FREE(rdata);
454                 SAFE_FREE(rparam);
455
456                 DEBUG(3,("received %d entries (eos=%d)\n",
457                          ff_searchcount,ff_eos));
458
459                 if (ff_searchcount > 0)
460                         loop_count = 0;
461
462                 First = False;
463         }
464
465         /* see if the server disconnected or the connection otherwise failed */
466         if (cli_is_error(cli)) {
467                 total_received = -1;
468         } else {
469                 /* no connection problem.  let user function add each entry */
470                 rdata_end = dirlist + dirlist_len;
471                 for (p=dirlist,i=0;i<total_received;i++) {
472                         p += interpret_long_filename(frame,
473                                                         cli,
474                                                         info_level,
475                                                         p,
476                                                         rdata_end,
477                                                         &finfo,
478                                                         NULL,
479                                                         NULL);
480                         if (!finfo.name) {
481                                 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
482                                         info_level));
483                                 break;
484                         }
485                         fn(cli->dfs_mountpoint, &finfo, Mask, state);
486                 }
487         }
488
489         /* free up the dirlist buffer and last name raw blob */
490         SAFE_FREE(dirlist);
491         data_blob_free(&last_name_raw);
492         SAFE_FREE(mask);
493         TALLOC_FREE(frame);
494         return(total_received);
495 }
496
497 /****************************************************************************
498  Interpret a short filename structure.
499  The length of the structure is returned.
500 ****************************************************************************/
501
502 static bool interpret_short_filename(TALLOC_CTX *ctx,
503                                 struct cli_state *cli,
504                                 char *p,
505                                 struct file_info *finfo)
506 {
507         size_t ret;
508         ZERO_STRUCTP(finfo);
509
510         finfo->mode = CVAL(p,21);
511
512         /* this date is converted to GMT by make_unix_date */
513         finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
514         finfo->ctime_ts.tv_nsec = 0;
515         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
516         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
517         finfo->size = IVAL(p,26);
518         ret = clistr_pull_talloc(ctx,
519                         cli->inbuf,
520                         &finfo->name,
521                         p+30,
522                         12,
523                         STR_ASCII);
524         if (ret == (size_t)-1) {
525                 return false;
526         }
527
528         if (finfo->name) {
529                 strlcpy(finfo->short_name,
530                         finfo->name,
531                         sizeof(finfo->short_name));
532         }
533         return true;
534 }
535
536 /****************************************************************************
537  Do a directory listing, calling fn on each file found.
538  this uses the old SMBsearch interface. It is needed for testing Samba,
539  but should otherwise not be used.
540 ****************************************************************************/
541
542 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
543                  void (*fn)(const char *, struct file_info *, const char *,
544                             void *), void *state)
545 {
546         char *p;
547         int received = 0;
548         bool first = True;
549         char status[21];
550         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
551         int num_received = 0;
552         int i;
553         char *dirlist = NULL;
554         char *mask = NULL;
555         TALLOC_CTX *frame = NULL;
556
557         ZERO_ARRAY(status);
558
559         mask = SMB_STRDUP(Mask);
560         if (!mask) {
561                 return -1;
562         }
563
564         while (1) {
565                 memset(cli->outbuf,'\0',smb_size);
566                 memset(cli->inbuf,'\0',smb_size);
567
568                 cli_set_message(cli->outbuf,2,0,True);
569
570                 SCVAL(cli->outbuf,smb_com,SMBsearch);
571
572                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
573                 cli_setup_packet(cli);
574
575                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
576                 SSVAL(cli->outbuf,smb_vwv1,attribute);
577
578                 p = smb_buf(cli->outbuf);
579                 *p++ = 4;
580
581                 p += clistr_push(cli, p, first?mask:"",
582                                 cli->bufsize - PTR_DIFF(p,cli->outbuf),
583                                 STR_TERMINATE);
584                 *p++ = 5;
585                 if (first) {
586                         SSVAL(p,0,0);
587                         p += 2;
588                 } else {
589                         SSVAL(p,0,21);
590                         p += 2;
591                         memcpy(p,status,21);
592                         p += 21;
593                 }
594
595                 cli_setup_bcc(cli, p);
596                 cli_send_smb(cli);
597                 if (!cli_receive_smb(cli)) break;
598
599                 received = SVAL(cli->inbuf,smb_vwv0);
600                 if (received <= 0) break;
601
602                 /* Ensure we received enough data. */
603                 if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
604                                 received*DIR_STRUCT_SIZE) {
605                         break;
606                 }
607
608                 first = False;
609
610                 dirlist = (char *)SMB_REALLOC(
611                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
612                 if (!dirlist) {
613                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
614                         SAFE_FREE(mask);
615                         return 0;
616                 }
617
618                 p = smb_buf(cli->inbuf) + 3;
619
620                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
621                        p,received*DIR_STRUCT_SIZE);
622
623                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
624
625                 num_received += received;
626
627                 if (cli_is_error(cli)) break;
628         }
629
630         if (!first) {
631                 memset(cli->outbuf,'\0',smb_size);
632                 memset(cli->inbuf,'\0',smb_size);
633
634                 cli_set_message(cli->outbuf,2,0,True);
635                 SCVAL(cli->outbuf,smb_com,SMBfclose);
636                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
637                 cli_setup_packet(cli);
638
639                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
640                 SSVAL(cli->outbuf, smb_vwv1, attribute);
641
642                 p = smb_buf(cli->outbuf);
643                 *p++ = 4;
644                 fstrcpy(p, "");
645                 p += strlen(p) + 1;
646                 *p++ = 5;
647                 SSVAL(p, 0, 21);
648                 p += 2;
649                 memcpy(p,status,21);
650                 p += 21;
651
652                 cli_setup_bcc(cli, p);
653                 cli_send_smb(cli);
654                 if (!cli_receive_smb(cli)) {
655                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
656                 }
657         }
658
659         frame = talloc_stackframe();
660         for (p=dirlist,i=0;i<num_received;i++) {
661                 struct file_info finfo;
662                 if (!interpret_short_filename(frame, cli, p, &finfo)) {
663                         break;
664                 }
665                 p += DIR_STRUCT_SIZE;
666                 fn("\\", &finfo, Mask, state);
667         }
668         TALLOC_FREE(frame);
669
670         SAFE_FREE(mask);
671         SAFE_FREE(dirlist);
672         return(num_received);
673 }
674
675 /****************************************************************************
676  Do a directory listing, calling fn on each file found.
677  This auto-switches between old and new style.
678 ****************************************************************************/
679
680 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
681              void (*fn)(const char *, struct file_info *, const char *,
682                         void *), void *state)
683 {
684         if (cli->protocol <= PROTOCOL_LANMAN1)
685                 return cli_list_old(cli, Mask, attribute, fn, state);
686         return cli_list_new(cli, Mask, attribute, fn, state);
687 }