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