a bunch of fixes from the s\7fflight to seattle
[kai/samba.git] / source3 / libsmb / clitrans.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    client transaction calls
5    Copyright (C) Andrew Tridgell 1994-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define NO_SYSLOG
23
24 #include "includes.h"
25
26
27 /****************************************************************************
28   send a SMB trans or trans2 request
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, int lsetup, int msetup,
34                     char *param, int lparam, int mparam,
35                     char *data, int ldata, int mdata)
36 {
37         int i;
38         int this_ldata,this_lparam;
39         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         CVAL(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         cli_send_smb(cli);
88
89         if (this_ldata < ldata || this_lparam < lparam) {
90                 /* receive interim response */
91                 if (!cli_receive_smb(cli) || 
92                     CVAL(cli->inbuf,smb_rcls) != 0) {
93                         return(False);
94                 }      
95
96                 tot_data = this_ldata;
97                 tot_param = this_lparam;
98                 
99                 while (tot_data < ldata || tot_param < lparam)  {
100                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
101                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
102
103                         set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
104                         CVAL(cli->outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2;
105                         
106                         outparam = smb_buf(cli->outbuf);
107                         outdata = outparam+this_lparam;
108                         
109                         /* secondary request */
110                         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
111                         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
112                         SSVAL(cli->outbuf,smb_spscnt,this_lparam);      /* pscnt */
113                         SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */
114                         SSVAL(cli->outbuf,smb_spsdisp,tot_param);       /* psdisp */
115                         SSVAL(cli->outbuf,smb_sdscnt,this_ldata);       /* dscnt */
116                         SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
117                         SSVAL(cli->outbuf,smb_sdsdisp,tot_data);        /* dsdisp */
118                         if (trans==SMBtrans2)
119                                 SSVALS(cli->outbuf,smb_sfid,fid);               /* fid */
120                         if (this_lparam)                        /* param[] */
121                                 memcpy(outparam,param+tot_param,this_lparam);
122                         if (this_ldata)                 /* data[] */
123                                 memcpy(outdata,data+tot_data,this_ldata);
124                         cli_setup_bcc(cli, outdata+this_ldata);
125                         
126                         show_msg(cli->outbuf);
127                         cli_send_smb(cli);
128                         
129                         tot_data += this_ldata;
130                         tot_param += this_lparam;
131                 }
132         }
133
134         return(True);
135 }
136
137
138 /****************************************************************************
139   receive a SMB trans or trans2 response allocating the necessary memory
140   ****************************************************************************/
141 BOOL cli_receive_trans(struct cli_state *cli,int trans,
142                               char **param, int *param_len,
143                               char **data, int *data_len)
144 {
145         int total_data=0;
146         int total_param=0;
147         int this_data,this_param;
148         uint8 eclass;
149         uint32 ecode;
150         char *tdata;
151         char *tparam;
152
153         *data_len = *param_len = 0;
154
155         if (!cli_receive_smb(cli))
156                 return False;
157
158         show_msg(cli->inbuf);
159         
160         /* sanity check */
161         if (CVAL(cli->inbuf,smb_com) != trans) {
162                 DEBUG(0,("Expected %s response, got command 0x%02x\n",
163                          trans==SMBtrans?"SMBtrans":"SMBtrans2", 
164                          CVAL(cli->inbuf,smb_com)));
165                 return(False);
166         }
167
168         /*
169          * An NT RPC pipe call can return ERRDOS, ERRmoredata
170          * to a trans call. This is not an error and should not
171          * be treated as such.
172          */
173
174         if (cli_is_error(cli)) {
175                 if (cli_is_dos_error(cli)) {
176                         cli_dos_error(cli, &eclass, &ecode);
177
178                         if(cli->nt_pipe_fnum == 0)
179                                 return(False);
180
181                         if(!(eclass == ERRDOS && ecode == ERRmoredata)) {
182                                 if (eclass != 0 && (ecode != (0x80000000 | STATUS_BUFFER_OVERFLOW)))
183                                         return(False);
184                         }
185                 }
186                 return False;
187         }
188
189         /* parse out the lengths */
190         total_data = SVAL(cli->inbuf,smb_tdrcnt);
191         total_param = SVAL(cli->inbuf,smb_tprcnt);
192
193         /* allocate it */
194         if (total_data!=0) {
195                 tdata = Realloc(*data,total_data);
196                 if (!tdata) {
197                         DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
198                         return False;
199                 }
200                 else
201                         *data = tdata;
202         }
203
204         if (total_param!=0) {
205                 tparam = Realloc(*param,total_param);
206                 if (!tparam) {
207                         DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
208                         return False;
209                 }
210                 else
211                         *param = tparam;
212         }
213
214         while (1)  {
215                 this_data = SVAL(cli->inbuf,smb_drcnt);
216                 this_param = SVAL(cli->inbuf,smb_prcnt);
217
218                 if (this_data + *data_len > total_data ||
219                     this_param + *param_len > total_param) {
220                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
221                         return False;
222                 }
223
224                 if (this_data)
225                         memcpy(*data + SVAL(cli->inbuf,smb_drdisp),
226                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff),
227                                this_data);
228                 if (this_param)
229                         memcpy(*param + SVAL(cli->inbuf,smb_prdisp),
230                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff),
231                                this_param);
232                 *data_len += this_data;
233                 *param_len += this_param;
234
235                 /* parse out the total lengths again - they can shrink! */
236                 total_data = SVAL(cli->inbuf,smb_tdrcnt);
237                 total_param = SVAL(cli->inbuf,smb_tprcnt);
238                 
239                 if (total_data <= *data_len && total_param <= *param_len)
240                         break;
241                 
242                 if (!cli_receive_smb(cli))
243                         return False;
244
245                 show_msg(cli->inbuf);
246                 
247                 /* sanity check */
248                 if (CVAL(cli->inbuf,smb_com) != trans) {
249                         DEBUG(0,("Expected %s response, got command 0x%02x\n",
250                                  trans==SMBtrans?"SMBtrans":"SMBtrans2", 
251                                  CVAL(cli->inbuf,smb_com)));
252                         return(False);
253                 }
254                 if (cli_is_dos_error(cli)) {
255                         cli_dos_error(cli, &eclass, &ecode);
256                         if(cli->nt_pipe_fnum == 0 || 
257                            !(eclass == ERRDOS && ecode == ERRmoredata))
258                                 return(False);
259                 }
260         }
261         
262         return(True);
263 }
264
265
266
267
268 /****************************************************************************
269   send a SMB nttrans request
270   ****************************************************************************/
271 BOOL cli_send_nt_trans(struct cli_state *cli, 
272                        int function, 
273                        int flags,
274                        uint16 *setup, int lsetup, int msetup,
275                        char *param, int lparam, int mparam,
276                        char *data, int ldata, int mdata)
277 {
278         int i;
279         int this_ldata,this_lparam;
280         int tot_data=0,tot_param=0;
281         char *outdata,*outparam;
282
283         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
284         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
285
286         memset(cli->outbuf,'\0',smb_size);
287         set_message(cli->outbuf,19+lsetup,0,True);
288         CVAL(cli->outbuf,smb_com) = SMBnttrans;
289         SSVAL(cli->outbuf,smb_tid, cli->cnum);
290         cli_setup_packet(cli);
291
292         outparam = smb_buf(cli->outbuf)+3;
293         outdata = outparam+this_lparam;
294
295         /* primary request */
296         SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
297         SCVAL(cli->outbuf,smb_nt_Flags,flags);
298         SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
299         SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
300         SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
301         SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
302         SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
303         SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
304         SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
305         SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
306         SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
307         SIVAL(cli->outbuf,smb_nt_Function, function);
308         for (i=0;i<lsetup;i++)          /* setup[] */
309                 SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
310         
311         if (this_lparam)                        /* param[] */
312                 memcpy(outparam,param,this_lparam);
313         if (this_ldata)                 /* data[] */
314                 memcpy(outdata,data,this_ldata);
315
316         cli_setup_bcc(cli, outdata+this_ldata);
317
318         show_msg(cli->outbuf);
319         cli_send_smb(cli);
320
321         if (this_ldata < ldata || this_lparam < lparam) {
322                 /* receive interim response */
323                 if (!cli_receive_smb(cli) || 
324                     CVAL(cli->inbuf,smb_rcls) != 0) {
325                         return(False);
326                 }      
327
328                 tot_data = this_ldata;
329                 tot_param = this_lparam;
330                 
331                 while (tot_data < ldata || tot_param < lparam)  {
332                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
333                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
334
335                         set_message(cli->outbuf,18,0,True);
336                         CVAL(cli->outbuf,smb_com) = SMBnttranss;
337
338                         /* XXX - these should probably be aligned */
339                         outparam = smb_buf(cli->outbuf);
340                         outdata = outparam+this_lparam;
341                         
342                         /* secondary request */
343                         SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
344                         SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
345                         SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
346                         SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
347                         SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
348                         SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
349                         SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
350                         SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
351                         if (this_lparam)                        /* param[] */
352                                 memcpy(outparam,param+tot_param,this_lparam);
353                         if (this_ldata)                 /* data[] */
354                                 memcpy(outdata,data+tot_data,this_ldata);
355                         cli_setup_bcc(cli, outdata+this_ldata);
356                         
357                         show_msg(cli->outbuf);
358                         cli_send_smb(cli);
359                         
360                         tot_data += this_ldata;
361                         tot_param += this_lparam;
362                 }
363         }
364
365         return(True);
366 }
367
368
369
370 /****************************************************************************
371   receive a SMB nttrans response allocating the necessary memory
372   ****************************************************************************/
373 BOOL cli_receive_nt_trans(struct cli_state *cli,
374                           char **param, int *param_len,
375                           char **data, int *data_len)
376 {
377         int total_data=0;
378         int total_param=0;
379         int this_data,this_param;
380         uint8 eclass;
381         uint32 ecode;
382         char *tdata;
383         char *tparam;
384
385         *data_len = *param_len = 0;
386
387         if (!cli_receive_smb(cli))
388                 return False;
389
390         show_msg(cli->inbuf);
391         
392         /* sanity check */
393         if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
394                 DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
395                          CVAL(cli->inbuf,smb_com)));
396                 return(False);
397         }
398
399         /*
400          * An NT RPC pipe call can return ERRDOS, ERRmoredata
401          * to a trans call. This is not an error and should not
402          * be treated as such.
403          */
404         if (cli_is_dos_error(cli)) {
405                 cli_dos_error(cli, &eclass, &ecode);
406                 if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
407                         return(False);
408         }
409
410         /* parse out the lengths */
411         total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
412         total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
413
414         /* allocate it */
415         tdata = Realloc(*data,total_data);
416         if (!tdata) {
417                 DEBUG(0,("cli_receive_nt_trans: failed to enlarge buffer"));
418                 return False;
419         }
420         else
421                 *data = tdata;
422         tparam = Realloc(*param,total_param);
423         if (!tparam) {
424                 DEBUG(0,("cli_receive_nt_trans: failed to enlarge buffer"));
425                 return False;
426         }
427         else
428                 *param = tparam;
429
430         while (1)  {
431                 this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
432                 this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
433
434                 if (this_data + *data_len > total_data ||
435                     this_param + *param_len > total_param) {
436                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
437                         return False;
438                 }
439
440                 if (this_data)
441                         memcpy(*data + SVAL(cli->inbuf,smb_ntr_DataDisplacement),
442                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_DataOffset),
443                                this_data);
444                 if (this_param)
445                         memcpy(*param + SVAL(cli->inbuf,smb_ntr_ParameterDisplacement),
446                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_ParameterOffset),
447                                this_param);
448                 *data_len += this_data;
449                 *param_len += this_param;
450
451                 /* parse out the total lengths again - they can shrink! */
452                 total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
453                 total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
454                 
455                 if (total_data <= *data_len && total_param <= *param_len)
456                         break;
457                 
458                 if (!cli_receive_smb(cli))
459                         return False;
460
461                 show_msg(cli->inbuf);
462                 
463                 /* sanity check */
464                 if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
465                         DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
466                                  CVAL(cli->inbuf,smb_com)));
467                         return(False);
468                 }
469                 if (cli_is_dos_error(cli)) {
470                         cli_dos_error(cli, &eclass, &ecode);
471                         if(cli->nt_pipe_fnum == 0 || 
472                            !(eclass == ERRDOS && ecode == ERRmoredata))
473                                 return(False);
474                 }
475         }
476         
477         return(True);
478 }