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