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