When re-writing tdb version numbers as little endian int32, we must
[ira/wip.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                     cli_is_error(cli)) {
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         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
178         /* parse out the lengths */
179         total_data = SVAL(cli->inbuf,smb_tdrcnt);
180         total_param = SVAL(cli->inbuf,smb_tprcnt);
181
182         /* allocate it */
183         if (total_data!=0) {
184                 tdata = Realloc(*data,total_data);
185                 if (!tdata) {
186                         DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
187                         return False;
188                 }
189                 else
190                         *data = tdata;
191         }
192
193         if (total_param!=0) {
194                 tparam = Realloc(*param,total_param);
195                 if (!tparam) {
196                         DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
197                         return False;
198                 }
199                 else
200                         *param = tparam;
201         }
202
203         while (1)  {
204                 this_data = SVAL(cli->inbuf,smb_drcnt);
205                 this_param = SVAL(cli->inbuf,smb_prcnt);
206
207                 if (this_data + *data_len > total_data ||
208                     this_param + *param_len > total_param) {
209                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
210                         return False;
211                 }
212
213                 if (this_data)
214                         memcpy(*data + SVAL(cli->inbuf,smb_drdisp),
215                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff),
216                                this_data);
217                 if (this_param)
218                         memcpy(*param + SVAL(cli->inbuf,smb_prdisp),
219                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff),
220                                this_param);
221                 *data_len += this_data;
222                 *param_len += this_param;
223
224                 /* parse out the total lengths again - they can shrink! */
225                 total_data = SVAL(cli->inbuf,smb_tdrcnt);
226                 total_param = SVAL(cli->inbuf,smb_tprcnt);
227                 
228                 if (total_data <= *data_len && total_param <= *param_len)
229                         break;
230                 
231                 if (!cli_receive_smb(cli))
232                         return False;
233
234                 show_msg(cli->inbuf);
235                 
236                 /* sanity check */
237                 if (CVAL(cli->inbuf,smb_com) != trans) {
238                         DEBUG(0,("Expected %s response, got command 0x%02x\n",
239                                  trans==SMBtrans?"SMBtrans":"SMBtrans2", 
240                                  CVAL(cli->inbuf,smb_com)));
241                         return(False);
242                 }
243                 if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
244                         return(False);
245                 }
246         }
247         
248         return(True);
249 }
250
251
252
253
254 /****************************************************************************
255   send a SMB nttrans request
256   ****************************************************************************/
257 BOOL cli_send_nt_trans(struct cli_state *cli, 
258                        int function, 
259                        int flags,
260                        uint16 *setup, int lsetup, int msetup,
261                        char *param, int lparam, int mparam,
262                        char *data, int ldata, int mdata)
263 {
264         int i;
265         int this_ldata,this_lparam;
266         int tot_data=0,tot_param=0;
267         char *outdata,*outparam;
268
269         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
270         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
271
272         memset(cli->outbuf,'\0',smb_size);
273         set_message(cli->outbuf,19+lsetup,0,True);
274         CVAL(cli->outbuf,smb_com) = SMBnttrans;
275         SSVAL(cli->outbuf,smb_tid, cli->cnum);
276         cli_setup_packet(cli);
277
278         outparam = smb_buf(cli->outbuf)+3;
279         outdata = outparam+this_lparam;
280
281         /* primary request */
282         SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
283         SCVAL(cli->outbuf,smb_nt_Flags,flags);
284         SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
285         SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
286         SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
287         SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
288         SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
289         SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
290         SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
291         SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
292         SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
293         SIVAL(cli->outbuf,smb_nt_Function, function);
294         for (i=0;i<lsetup;i++)          /* setup[] */
295                 SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
296         
297         if (this_lparam)                        /* param[] */
298                 memcpy(outparam,param,this_lparam);
299         if (this_ldata)                 /* data[] */
300                 memcpy(outdata,data,this_ldata);
301
302         cli_setup_bcc(cli, outdata+this_ldata);
303
304         show_msg(cli->outbuf);
305         cli_send_smb(cli);
306
307         if (this_ldata < ldata || this_lparam < lparam) {
308                 /* receive interim response */
309                 if (!cli_receive_smb(cli) || 
310                     cli_is_error(cli)) {
311                         return(False);
312                 }      
313
314                 tot_data = this_ldata;
315                 tot_param = this_lparam;
316                 
317                 while (tot_data < ldata || tot_param < lparam)  {
318                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
319                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
320
321                         set_message(cli->outbuf,18,0,True);
322                         CVAL(cli->outbuf,smb_com) = SMBnttranss;
323
324                         /* XXX - these should probably be aligned */
325                         outparam = smb_buf(cli->outbuf);
326                         outdata = outparam+this_lparam;
327                         
328                         /* secondary request */
329                         SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
330                         SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
331                         SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
332                         SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
333                         SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
334                         SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
335                         SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
336                         SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
337                         if (this_lparam)                        /* param[] */
338                                 memcpy(outparam,param+tot_param,this_lparam);
339                         if (this_ldata)                 /* data[] */
340                                 memcpy(outdata,data+tot_data,this_ldata);
341                         cli_setup_bcc(cli, outdata+this_ldata);
342                         
343                         show_msg(cli->outbuf);
344                         cli_send_smb(cli);
345                         
346                         tot_data += this_ldata;
347                         tot_param += this_lparam;
348                 }
349         }
350
351         return(True);
352 }
353
354
355
356 /****************************************************************************
357   receive a SMB nttrans response allocating the necessary memory
358   ****************************************************************************/
359 BOOL cli_receive_nt_trans(struct cli_state *cli,
360                           char **param, int *param_len,
361                           char **data, int *data_len)
362 {
363         int total_data=0;
364         int total_param=0;
365         int this_data,this_param;
366         uint8 eclass;
367         uint32 ecode;
368         char *tdata;
369         char *tparam;
370
371         *data_len = *param_len = 0;
372
373         if (!cli_receive_smb(cli))
374                 return False;
375
376         show_msg(cli->inbuf);
377         
378         /* sanity check */
379         if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
380                 DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
381                          CVAL(cli->inbuf,smb_com)));
382                 return(False);
383         }
384
385         /*
386          * An NT RPC pipe call can return ERRDOS, ERRmoredata
387          * to a trans call. This is not an error and should not
388          * be treated as such.
389          */
390         if (cli_is_dos_error(cli)) {
391                 cli_dos_error(cli, &eclass, &ecode);
392                 if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
393                         return(False);
394         }
395
396         /* parse out the lengths */
397         total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
398         total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
399
400         /* allocate it */
401         if (total_data) {
402                 tdata = Realloc(*data,total_data);
403                 if (!tdata) {
404                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
405                         return False;
406                 } else {
407                         *data = tdata;
408                 }
409         }
410
411         if (total_param) {
412                 tparam = Realloc(*param,total_param);
413                 if (!tparam) {
414                         DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
415                         return False;
416                 } else {
417                         *param = tparam;
418                 }
419         }
420
421         while (1)  {
422                 this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
423                 this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
424
425                 if (this_data + *data_len > total_data ||
426                     this_param + *param_len > total_param) {
427                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
428                         return False;
429                 }
430
431                 if (this_data)
432                         memcpy(*data + SVAL(cli->inbuf,smb_ntr_DataDisplacement),
433                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_DataOffset),
434                                this_data);
435                 if (this_param)
436                         memcpy(*param + SVAL(cli->inbuf,smb_ntr_ParameterDisplacement),
437                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_ParameterOffset),
438                                this_param);
439                 *data_len += this_data;
440                 *param_len += this_param;
441
442                 /* parse out the total lengths again - they can shrink! */
443                 total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
444                 total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
445                 
446                 if (total_data <= *data_len && total_param <= *param_len)
447                         break;
448                 
449                 if (!cli_receive_smb(cli))
450                         return False;
451
452                 show_msg(cli->inbuf);
453                 
454                 /* sanity check */
455                 if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
456                         DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
457                                  CVAL(cli->inbuf,smb_com)));
458                         return(False);
459                 }
460                 if (cli_is_dos_error(cli)) {
461                         cli_dos_error(cli, &eclass, &ecode);
462                         if(cli->nt_pipe_fnum == 0 || 
463                            !(eclass == ERRDOS && ecode == ERRmoredata))
464                                 return(False);
465                 }
466         }
467         
468         return(True);
469 }