Removed version number from file header.
[samba.git] / source / libsmb / clitrans.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client transaction calls
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 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
26 /****************************************************************************
27   send a SMB trans or trans2 request
28   ****************************************************************************/
29 BOOL cli_send_trans(struct cli_state *cli, int trans, 
30                     const char *pipe_name, 
31                     int fid, int flags,
32                     uint16 *setup, int lsetup, int msetup,
33                     char *param, int lparam, int mparam,
34                     char *data, int ldata, int mdata)
35 {
36         int i;
37         int this_ldata,this_lparam;
38         int tot_data=0,tot_param=0;
39         char *outdata,*outparam;
40         char *p;
41         int pipe_name_len=0;
42
43         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
44         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
45
46         memset(cli->outbuf,'\0',smb_size);
47         set_message(cli->outbuf,14+lsetup,0,True);
48         SCVAL(cli->outbuf,smb_com,trans);
49         SSVAL(cli->outbuf,smb_tid, cli->cnum);
50         cli_setup_packet(cli);
51
52         if (pipe_name) {
53                 pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE);
54         }
55
56         outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3);
57         outdata = outparam+this_lparam;
58
59         /* primary request */
60         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
61         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
62         SSVAL(cli->outbuf,smb_mprcnt,mparam);   /* mprcnt */
63         SSVAL(cli->outbuf,smb_mdrcnt,mdata);    /* mdrcnt */
64         SCVAL(cli->outbuf,smb_msrcnt,msetup);   /* msrcnt */
65         SSVAL(cli->outbuf,smb_flags,flags);     /* flags */
66         SIVAL(cli->outbuf,smb_timeout,0);               /* timeout */
67         SSVAL(cli->outbuf,smb_pscnt,this_lparam);       /* pscnt */
68         SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */
69         SSVAL(cli->outbuf,smb_dscnt,this_ldata);        /* dscnt */
70         SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
71         SCVAL(cli->outbuf,smb_suwcnt,lsetup);   /* suwcnt */
72         for (i=0;i<lsetup;i++)          /* setup[] */
73                 SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
74         p = smb_buf(cli->outbuf);
75         if (trans != SMBtrans) {
76                 *p++ = 0;  /* put in a null smb_name */
77                 *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
78         }
79         if (this_lparam)                        /* param[] */
80                 memcpy(outparam,param,this_lparam);
81         if (this_ldata)                 /* data[] */
82                 memcpy(outdata,data,this_ldata);
83         cli_setup_bcc(cli, outdata+this_ldata);
84
85         show_msg(cli->outbuf);
86         cli_send_smb(cli);
87
88         if (this_ldata < ldata || this_lparam < lparam) {
89                 /* receive interim response */
90                 if (!cli_receive_smb(cli) || 
91                     cli_is_error(cli)) {
92                         return(False);
93                 }      
94
95                 tot_data = this_ldata;
96                 tot_param = this_lparam;
97                 
98                 while (tot_data < ldata || tot_param < lparam)  {
99                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
100                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
101
102                         set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
103                         SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2));
104                         
105                         outparam = smb_buf(cli->outbuf);
106                         outdata = outparam+this_lparam;
107                         
108                         /* secondary request */
109                         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
110                         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
111                         SSVAL(cli->outbuf,smb_spscnt,this_lparam);      /* pscnt */
112                         SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */
113                         SSVAL(cli->outbuf,smb_spsdisp,tot_param);       /* psdisp */
114                         SSVAL(cli->outbuf,smb_sdscnt,this_ldata);       /* dscnt */
115                         SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
116                         SSVAL(cli->outbuf,smb_sdsdisp,tot_data);        /* dsdisp */
117                         if (trans==SMBtrans2)
118                                 SSVALS(cli->outbuf,smb_sfid,fid);               /* fid */
119                         if (this_lparam)                        /* param[] */
120                                 memcpy(outparam,param+tot_param,this_lparam);
121                         if (this_ldata)                 /* data[] */
122                                 memcpy(outdata,data+tot_data,this_ldata);
123                         cli_setup_bcc(cli, outdata+this_ldata);
124                         
125                         show_msg(cli->outbuf);
126                         cli_send_smb(cli);
127                         
128                         tot_data += this_ldata;
129                         tot_param += this_lparam;
130                 }
131         }
132
133         return(True);
134 }
135
136
137 /****************************************************************************
138   receive a SMB trans or trans2 response allocating the necessary memory
139   ****************************************************************************/
140 BOOL cli_receive_trans(struct cli_state *cli,int trans,
141                               char **param, int *param_len,
142                               char **data, int *data_len)
143 {
144         int total_data=0;
145         int total_param=0;
146         int this_data,this_param;
147         NTSTATUS status;
148         char *tdata;
149         char *tparam;
150
151         *data_len = *param_len = 0;
152
153         if (!cli_receive_smb(cli))
154                 return False;
155
156         show_msg(cli->inbuf);
157         
158         /* sanity check */
159         if (CVAL(cli->inbuf,smb_com) != trans) {
160                 DEBUG(0,("Expected %s response, got command 0x%02x\n",
161                          trans==SMBtrans?"SMBtrans":"SMBtrans2", 
162                          CVAL(cli->inbuf,smb_com)));
163                 return(False);
164         }
165
166         /*
167          * An NT RPC pipe call can return ERRDOS, ERRmoredata
168          * to a trans call. This is not an error and should not
169          * be treated as such.
170          */
171         status = cli_nt_error(cli);
172         
173         if (NT_STATUS_IS_ERR(status)) {
174                 return False;
175         }
176
177         /* parse out the lengths */
178         total_data = SVAL(cli->inbuf,smb_tdrcnt);
179         total_param = SVAL(cli->inbuf,smb_tprcnt);
180
181         /* allocate it */
182         if (total_data!=0) {
183                 tdata = Realloc(*data,total_data);
184                 if (!tdata) {
185                         DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
186                         return False;
187                 }
188                 else
189                         *data = tdata;
190         }
191
192         if (total_param!=0) {
193                 tparam = Realloc(*param,total_param);
194                 if (!tparam) {
195                         DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
196                         return False;
197                 }
198                 else
199                         *param = tparam;
200         }
201
202         while (1)  {
203                 this_data = SVAL(cli->inbuf,smb_drcnt);
204                 this_param = SVAL(cli->inbuf,smb_prcnt);
205
206                 if (this_data + *data_len > total_data ||
207                     this_param + *param_len > total_param) {
208                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
209                         return False;
210                 }
211
212                 if (this_data)
213                         memcpy(*data + SVAL(cli->inbuf,smb_drdisp),
214                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff),
215                                this_data);
216                 if (this_param)
217                         memcpy(*param + SVAL(cli->inbuf,smb_prdisp),
218                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff),
219                                this_param);
220                 *data_len += this_data;
221                 *param_len += this_param;
222
223                 /* parse out the total lengths again - they can shrink! */
224                 total_data = SVAL(cli->inbuf,smb_tdrcnt);
225                 total_param = SVAL(cli->inbuf,smb_tprcnt);
226                 
227                 if (total_data <= *data_len && total_param <= *param_len)
228                         break;
229                 
230                 if (!cli_receive_smb(cli))
231                         return False;
232
233                 show_msg(cli->inbuf);
234                 
235                 /* sanity check */
236                 if (CVAL(cli->inbuf,smb_com) != trans) {
237                         DEBUG(0,("Expected %s response, got command 0x%02x\n",
238                                  trans==SMBtrans?"SMBtrans":"SMBtrans2", 
239                                  CVAL(cli->inbuf,smb_com)));
240                         return(False);
241                 }
242                 if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
243                         return(False);
244                 }
245         }
246         
247         return(True);
248 }
249
250
251
252
253 /****************************************************************************
254   send a SMB nttrans request
255   ****************************************************************************/
256 BOOL cli_send_nt_trans(struct cli_state *cli, 
257                        int function, 
258                        int flags,
259                        uint16 *setup, int lsetup, int msetup,
260                        char *param, int lparam, int mparam,
261                        char *data, int ldata, int mdata)
262 {
263         int i;
264         int this_ldata,this_lparam;
265         int tot_data=0,tot_param=0;
266         char *outdata,*outparam;
267
268         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
269         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
270
271         memset(cli->outbuf,'\0',smb_size);
272         set_message(cli->outbuf,19+lsetup,0,True);
273         SCVAL(cli->outbuf,smb_com,SMBnttrans);
274         SSVAL(cli->outbuf,smb_tid, cli->cnum);
275         cli_setup_packet(cli);
276
277         outparam = smb_buf(cli->outbuf)+3;
278         outdata = outparam+this_lparam;
279
280         /* primary request */
281         SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
282         SCVAL(cli->outbuf,smb_nt_Flags,flags);
283         SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
284         SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
285         SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
286         SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
287         SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
288         SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
289         SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
290         SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
291         SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
292         SIVAL(cli->outbuf,smb_nt_Function, function);
293         for (i=0;i<lsetup;i++)          /* setup[] */
294                 SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
295         
296         if (this_lparam)                        /* param[] */
297                 memcpy(outparam,param,this_lparam);
298         if (this_ldata)                 /* data[] */
299                 memcpy(outdata,data,this_ldata);
300
301         cli_setup_bcc(cli, outdata+this_ldata);
302
303         show_msg(cli->outbuf);
304         cli_send_smb(cli);
305
306         if (this_ldata < ldata || this_lparam < lparam) {
307                 /* receive interim response */
308                 if (!cli_receive_smb(cli) || 
309                     cli_is_error(cli)) {
310                         return(False);
311                 }      
312
313                 tot_data = this_ldata;
314                 tot_param = this_lparam;
315                 
316                 while (tot_data < ldata || tot_param < lparam)  {
317                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
318                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
319
320                         set_message(cli->outbuf,18,0,True);
321                         SCVAL(cli->outbuf,smb_com,SMBnttranss);
322
323                         /* XXX - these should probably be aligned */
324                         outparam = smb_buf(cli->outbuf);
325                         outdata = outparam+this_lparam;
326                         
327                         /* secondary request */
328                         SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
329                         SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
330                         SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
331                         SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
332                         SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
333                         SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
334                         SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
335                         SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
336                         if (this_lparam)                        /* param[] */
337                                 memcpy(outparam,param+tot_param,this_lparam);
338                         if (this_ldata)                 /* data[] */
339                                 memcpy(outdata,data+tot_data,this_ldata);
340                         cli_setup_bcc(cli, outdata+this_ldata);
341                         
342                         show_msg(cli->outbuf);
343                         cli_send_smb(cli);
344                         
345                         tot_data += this_ldata;
346                         tot_param += this_lparam;
347                 }
348         }
349
350         return(True);
351 }
352
353
354
355 /****************************************************************************
356   receive a SMB nttrans response allocating the necessary memory
357   ****************************************************************************/
358 BOOL cli_receive_nt_trans(struct cli_state *cli,
359                           char **param, int *param_len,
360                           char **data, int *data_len)
361 {
362         int total_data=0;
363         int total_param=0;
364         int this_data,this_param;
365         uint8 eclass;
366         uint32 ecode;
367         char *tdata;
368         char *tparam;
369
370         *data_len = *param_len = 0;
371
372         if (!cli_receive_smb(cli))
373                 return False;
374
375         show_msg(cli->inbuf);
376         
377         /* sanity check */
378         if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
379                 DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
380                          CVAL(cli->inbuf,smb_com)));
381                 return(False);
382         }
383
384         /*
385          * An NT RPC pipe call can return ERRDOS, ERRmoredata
386          * to a trans call. This is not an error and should not
387          * be treated as such.
388          */
389         if (cli_is_dos_error(cli)) {
390                 cli_dos_error(cli, &eclass, &ecode);
391                 if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
392                         return(False);
393         }
394
395         /* parse out the lengths */
396         total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
397         total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
398
399         /* allocate it */
400         if (total_data) {
401                 tdata = Realloc(*data,total_data);
402                 if (!tdata) {
403                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
404                         return False;
405                 } else {
406                         *data = tdata;
407                 }
408         }
409
410         if (total_param) {
411                 tparam = Realloc(*param,total_param);
412                 if (!tparam) {
413                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
414                         return False;
415                 } else {
416                         *param = tparam;
417                 }
418         }
419
420         while (1)  {
421                 this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
422                 this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
423
424                 if (this_data + *data_len > total_data ||
425                     this_param + *param_len > total_param) {
426                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
427                         return False;
428                 }
429
430                 if (this_data)
431                         memcpy(*data + SVAL(cli->inbuf,smb_ntr_DataDisplacement),
432                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_DataOffset),
433                                this_data);
434                 if (this_param)
435                         memcpy(*param + SVAL(cli->inbuf,smb_ntr_ParameterDisplacement),
436                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_ParameterOffset),
437                                this_param);
438                 *data_len += this_data;
439                 *param_len += this_param;
440
441                 /* parse out the total lengths again - they can shrink! */
442                 total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
443                 total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
444                 
445                 if (total_data <= *data_len && total_param <= *param_len)
446                         break;
447                 
448                 if (!cli_receive_smb(cli))
449                         return False;
450
451                 show_msg(cli->inbuf);
452                 
453                 /* sanity check */
454                 if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
455                         DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
456                                  CVAL(cli->inbuf,smb_com)));
457                         return(False);
458                 }
459                 if (cli_is_dos_error(cli)) {
460                         cli_dos_error(cli, &eclass, &ecode);
461                         if(cli->nt_pipe_fnum == 0 || 
462                            !(eclass == ERRDOS && ecode == ERRmoredata))
463                                 return(False);
464                 }
465         }
466         
467         return(True);
468 }