Merge from HEAD:
[samba.git] / source3 / 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
30 BOOL cli_send_trans(struct cli_state *cli, int trans, 
31                     const char *pipe_name, 
32                     int fid, int flags,
33                     uint16 *setup, unsigned int lsetup, unsigned int msetup,
34                     const char *param, unsigned int lparam, unsigned int mparam,
35                     const char *data, unsigned int ldata, unsigned int mdata)
36 {
37         unsigned int i;
38         unsigned int this_ldata,this_lparam;
39         unsigned int tot_data=0,tot_param=0;
40         char *outdata,*outparam;
41         char *p;
42         int pipe_name_len=0;
43
44         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
45         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
46
47         memset(cli->outbuf,'\0',smb_size);
48         set_message(cli->outbuf,14+lsetup,0,True);
49         SCVAL(cli->outbuf,smb_com,trans);
50         SSVAL(cli->outbuf,smb_tid, cli->cnum);
51         cli_setup_packet(cli);
52
53         if (pipe_name) {
54                 pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE);
55         }
56
57         outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3);
58         outdata = outparam+this_lparam;
59
60         /* primary request */
61         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
62         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
63         SSVAL(cli->outbuf,smb_mprcnt,mparam);   /* mprcnt */
64         SSVAL(cli->outbuf,smb_mdrcnt,mdata);    /* mdrcnt */
65         SCVAL(cli->outbuf,smb_msrcnt,msetup);   /* msrcnt */
66         SSVAL(cli->outbuf,smb_flags,flags);     /* flags */
67         SIVAL(cli->outbuf,smb_timeout,0);               /* timeout */
68         SSVAL(cli->outbuf,smb_pscnt,this_lparam);       /* pscnt */
69         SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */
70         SSVAL(cli->outbuf,smb_dscnt,this_ldata);        /* dscnt */
71         SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
72         SCVAL(cli->outbuf,smb_suwcnt,lsetup);   /* suwcnt */
73         for (i=0;i<lsetup;i++)          /* setup[] */
74                 SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
75         p = smb_buf(cli->outbuf);
76         if (trans != SMBtrans) {
77                 *p++ = 0;  /* put in a null smb_name */
78                 *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
79         }
80         if (this_lparam)                        /* param[] */
81                 memcpy(outparam,param,this_lparam);
82         if (this_ldata)                 /* data[] */
83                 memcpy(outdata,data,this_ldata);
84         cli_setup_bcc(cli, outdata+this_ldata);
85
86         show_msg(cli->outbuf);
87         if (!cli_send_smb(cli))
88                 return False;
89
90         if (this_ldata < ldata || this_lparam < lparam) {
91                 /* receive interim response */
92                 if (!cli_receive_smb(cli) || cli_is_error(cli))
93                         return(False);
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                         if (!cli_send_smb(cli))
127                                 return False;
128                         
129                         tot_data += this_ldata;
130                         tot_param += this_lparam;
131                 }
132         }
133
134         return(True);
135 }
136
137 /****************************************************************************
138  Receive a SMB trans or trans2 response allocating the necessary memory.
139 ****************************************************************************/
140
141 BOOL cli_receive_trans(struct cli_state *cli,int trans,
142                               char **param, unsigned int *param_len,
143                               char **data, unsigned int *data_len)
144 {
145         unsigned int total_data=0;
146         unsigned int total_param=0;
147         unsigned int this_data,this_param;
148         NTSTATUS status;
149         char *tdata;
150         char *tparam;
151
152         *data_len = *param_len = 0;
153
154         if (!cli_receive_smb(cli))
155                 return False;
156
157         show_msg(cli->inbuf);
158         
159         /* sanity check */
160         if (CVAL(cli->inbuf,smb_com) != trans) {
161                 DEBUG(0,("Expected %s response, got command 0x%02x\n",
162                          trans==SMBtrans?"SMBtrans":"SMBtrans2", 
163                          CVAL(cli->inbuf,smb_com)));
164                 return(False);
165         }
166
167         /*
168          * An NT RPC pipe call can return ERRDOS, ERRmoredata
169          * to a trans call. This is not an error and should not
170          * be treated as such.
171          */
172         status = cli_nt_error(cli);
173         
174         if (NT_STATUS_IS_ERR(status))
175                 return False;
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         for (;;)  {
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 + *data_len < this_data ||
213                                 this_data + *data_len < *data_len ||
214                                 this_param + *param_len < this_param ||
215                                 this_param + *param_len < *param_len) {
216                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
217                         return False;
218                 }
219
220                 if (this_data) {
221                         unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp);
222                         unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff);
223
224                         if (data_offset_out > total_data ||
225                                         data_offset_out + this_data > total_data ||
226                                         data_offset_out + this_data < data_offset_out ||
227                                         data_offset_out + this_data < this_data) {
228                                 DEBUG(1,("Data overflow in cli_receive_trans\n"));
229                                 return False;
230                         }
231                         if (data_offset_in > cli->bufsize ||
232                                         data_offset_in + this_data >  cli->bufsize ||
233                                         data_offset_in + this_data < data_offset_in ||
234                                         data_offset_in + this_data < this_data) {
235                                 DEBUG(1,("Data overflow in cli_receive_trans\n"));
236                                 return False;
237                         }
238
239                         memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
240                 }
241                 if (this_param) {
242                         unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp);
243                         unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff);
244
245                         if (param_offset_out > total_param ||
246                                         param_offset_out + this_param > total_param ||
247                                         param_offset_out + this_param < param_offset_out ||
248                                         param_offset_out + this_param < this_param) {
249                                 DEBUG(1,("Param overflow in cli_receive_trans\n"));
250                                 return False;
251                         }
252                         if (param_offset_in > cli->bufsize ||
253                                         param_offset_in + this_param >  cli->bufsize ||
254                                         param_offset_in + this_param < param_offset_in ||
255                                         param_offset_in + this_param < this_param) {
256                                 DEBUG(1,("Param overflow in cli_receive_trans\n"));
257                                 return False;
258                         }
259
260                         memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
261                 }
262                 *data_len += this_data;
263                 *param_len += this_param;
264
265                 if (total_data <= *data_len && total_param <= *param_len)
266                         break;
267                 
268                 if (!cli_receive_smb(cli))
269                         return False;
270
271                 show_msg(cli->inbuf);
272                 
273                 /* sanity check */
274                 if (CVAL(cli->inbuf,smb_com) != trans) {
275                         DEBUG(0,("Expected %s response, got command 0x%02x\n",
276                                  trans==SMBtrans?"SMBtrans":"SMBtrans2", 
277                                  CVAL(cli->inbuf,smb_com)));
278                         return(False);
279                 }
280                 if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
281                         return(False);
282                 }
283
284                 /* parse out the total lengths again - they can shrink! */
285                 if (SVAL(cli->inbuf,smb_tdrcnt) < total_data)
286                         total_data = SVAL(cli->inbuf,smb_tdrcnt);
287                 if (SVAL(cli->inbuf,smb_tprcnt) < total_param)
288                         total_param = SVAL(cli->inbuf,smb_tprcnt);
289                 
290                 if (total_data <= *data_len && total_param <= *param_len)
291                         break;
292                 
293         }
294         
295         return(True);
296 }
297
298 /****************************************************************************
299  Send a SMB nttrans request.
300 ****************************************************************************/
301
302 BOOL cli_send_nt_trans(struct cli_state *cli, 
303                        int function, 
304                        int flags,
305                        uint16 *setup, unsigned int lsetup, unsigned int msetup,
306                        char *param, unsigned int lparam, unsigned int mparam,
307                        char *data, unsigned int ldata, unsigned int mdata)
308 {
309         unsigned int i;
310         unsigned int this_ldata,this_lparam;
311         unsigned int tot_data=0,tot_param=0;
312         char *outdata,*outparam;
313
314         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
315         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
316
317         memset(cli->outbuf,'\0',smb_size);
318         set_message(cli->outbuf,19+lsetup,0,True);
319         SCVAL(cli->outbuf,smb_com,SMBnttrans);
320         SSVAL(cli->outbuf,smb_tid, cli->cnum);
321         cli_setup_packet(cli);
322
323         outparam = smb_buf(cli->outbuf)+3;
324         outdata = outparam+this_lparam;
325
326         /* primary request */
327         SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
328         SCVAL(cli->outbuf,smb_nt_Flags,flags);
329         SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
330         SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
331         SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
332         SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
333         SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
334         SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
335         SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
336         SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
337         SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
338         SIVAL(cli->outbuf,smb_nt_Function, function);
339         for (i=0;i<lsetup;i++)          /* setup[] */
340                 SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
341         
342         if (this_lparam)                        /* param[] */
343                 memcpy(outparam,param,this_lparam);
344         if (this_ldata)                 /* data[] */
345                 memcpy(outdata,data,this_ldata);
346
347         cli_setup_bcc(cli, outdata+this_ldata);
348
349         show_msg(cli->outbuf);
350         if (!cli_send_smb(cli))
351                 return False;
352
353         if (this_ldata < ldata || this_lparam < lparam) {
354                 /* receive interim response */
355                 if (!cli_receive_smb(cli) || cli_is_error(cli))
356                         return(False);
357
358                 tot_data = this_ldata;
359                 tot_param = this_lparam;
360                 
361                 while (tot_data < ldata || tot_param < lparam)  {
362                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
363                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
364
365                         set_message(cli->outbuf,18,0,True);
366                         SCVAL(cli->outbuf,smb_com,SMBnttranss);
367
368                         /* XXX - these should probably be aligned */
369                         outparam = smb_buf(cli->outbuf);
370                         outdata = outparam+this_lparam;
371                         
372                         /* secondary request */
373                         SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
374                         SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
375                         SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
376                         SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
377                         SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
378                         SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
379                         SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
380                         SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
381                         if (this_lparam)                        /* param[] */
382                                 memcpy(outparam,param+tot_param,this_lparam);
383                         if (this_ldata)                 /* data[] */
384                                 memcpy(outdata,data+tot_data,this_ldata);
385                         cli_setup_bcc(cli, outdata+this_ldata);
386                         
387                         show_msg(cli->outbuf);
388                         if (!cli_send_smb(cli))
389                                 return False;
390                         
391                         tot_data += this_ldata;
392                         tot_param += this_lparam;
393                 }
394         }
395
396         return(True);
397 }
398
399
400
401 /****************************************************************************
402   receive a SMB nttrans response allocating the necessary memory
403   ****************************************************************************/
404
405 BOOL cli_receive_nt_trans(struct cli_state *cli,
406                           char **param, unsigned int *param_len,
407                           char **data, unsigned int *data_len)
408 {
409         unsigned int total_data=0;
410         unsigned int total_param=0;
411         unsigned int this_data,this_param;
412         uint8 eclass;
413         uint32 ecode;
414         char *tdata;
415         char *tparam;
416
417         *data_len = *param_len = 0;
418
419         if (!cli_receive_smb(cli))
420                 return False;
421
422         show_msg(cli->inbuf);
423         
424         /* sanity check */
425         if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
426                 DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
427                          CVAL(cli->inbuf,smb_com)));
428                 return(False);
429         }
430
431         /*
432          * An NT RPC pipe call can return ERRDOS, ERRmoredata
433          * to a trans call. This is not an error and should not
434          * be treated as such.
435          */
436         if (cli_is_dos_error(cli)) {
437                 cli_dos_error(cli, &eclass, &ecode);
438                 if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
439                         return(False);
440         }
441
442         /* parse out the lengths */
443         total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
444         total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
445
446         /* allocate it */
447         if (total_data) {
448                 tdata = Realloc(*data,total_data);
449                 if (!tdata) {
450                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
451                         return False;
452                 } else {
453                         *data = tdata;
454                 }
455         }
456
457         if (total_param) {
458                 tparam = Realloc(*param,total_param);
459                 if (!tparam) {
460                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
461                         return False;
462                 } else {
463                         *param = tparam;
464                 }
465         }
466
467         while (1)  {
468                 this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
469                 this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
470
471                 if (this_data + *data_len > total_data ||
472                     this_param + *param_len > total_param) {
473                         DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
474                         return False;
475                 }
476
477                 if (this_data + *data_len < this_data ||
478                                 this_data + *data_len < *data_len ||
479                                 this_param + *param_len < this_param ||
480                                 this_param + *param_len < *param_len) {
481                         DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
482                         return False;
483                 }
484
485                 if (this_data) {
486                         unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement);
487                         unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset);
488
489                         if (data_offset_out > total_data ||
490                                         data_offset_out + this_data > total_data ||
491                                         data_offset_out + this_data < data_offset_out ||
492                                         data_offset_out + this_data < this_data) {
493                                 DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
494                                 return False;
495                         }
496                         if (data_offset_in > cli->bufsize ||
497                                         data_offset_in + this_data >  cli->bufsize ||
498                                         data_offset_in + this_data < data_offset_in ||
499                                         data_offset_in + this_data < this_data) {
500                                 DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
501                                 return False;
502                         }
503
504                         memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
505                 }
506
507                 if (this_param) {
508                         unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement);
509                         unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset);
510
511                         if (param_offset_out > total_param ||
512                                         param_offset_out + this_param > total_param ||
513                                         param_offset_out + this_param < param_offset_out ||
514                                         param_offset_out + this_param < this_param) {
515                                 DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
516                                 return False;
517                         }
518                         if (param_offset_in > cli->bufsize ||
519                                         param_offset_in + this_param >  cli->bufsize ||
520                                         param_offset_in + this_param < param_offset_in ||
521                                         param_offset_in + this_param < this_param) {
522                                 DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
523                                 return False;
524                         }
525
526                         memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
527                 }
528
529                 *data_len += this_data;
530                 *param_len += this_param;
531
532                 if (total_data <= *data_len && total_param <= *param_len)
533                         break;
534                 
535                 if (!cli_receive_smb(cli))
536                         return False;
537
538                 show_msg(cli->inbuf);
539                 
540                 /* sanity check */
541                 if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
542                         DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
543                                  CVAL(cli->inbuf,smb_com)));
544                         return(False);
545                 }
546                 if (cli_is_dos_error(cli)) {
547                         cli_dos_error(cli, &eclass, &ecode);
548                         if(cli->nt_pipe_fnum == 0 || 
549                            !(eclass == ERRDOS && ecode == ERRmoredata))
550                                 return(False);
551                 }
552                 /* parse out the total lengths again - they can shrink! */
553                 if (SVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data)
554                         total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
555                 if (SVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param)
556                         total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
557                 
558                 if (total_data <= *data_len && total_param <= *param_len)
559                         break;
560         }
561         
562         return(True);
563 }