• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/libcli/raw/
1/*
2   Unix SMB/CIFS implementation.
3   raw trans/trans2/nttrans operations
4
5   Copyright (C) James Myers 2003 <myersjj@samba.org>
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 3 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, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "includes.h"
22#include "../lib/util/dlinklist.h"
23#include "libcli/raw/libcliraw.h"
24#include "libcli/raw/raw_proto.h"
25
26#define TORTURE_TRANS_DATA 0
27
28/*
29  check out of bounds for incoming data
30*/
31static bool raw_trans_oob(struct smbcli_request *req,
32			  uint_t offset, uint_t count)
33{
34	uint8_t *ptr;
35
36	if (count == 0) {
37		return false;
38	}
39
40	ptr = req->in.hdr + offset;
41
42	/* be careful with wraparound! */
43	if ((uintptr_t)ptr < (uintptr_t)req->in.data ||
44	    (uintptr_t)ptr >= (uintptr_t)req->in.data + req->in.data_size ||
45	    count > req->in.data_size ||
46	    (uintptr_t)ptr + count > (uintptr_t)req->in.data + req->in.data_size) {
47		return true;
48	}
49	return false;
50}
51
52static size_t raw_trans_space_left(struct smbcli_request *req)
53{
54	if (req->transport->negotiate.max_xmit <= req->out.size) {
55		return 0;
56	}
57
58	return req->transport->negotiate.max_xmit - req->out.size;
59}
60
61struct smb_raw_trans2_recv_state {
62	uint8_t command;
63	uint32_t params_total;
64	uint32_t data_total;
65	uint32_t params_left;
66	uint32_t data_left;
67	bool got_first;
68	uint32_t recvd_data;
69	uint32_t recvd_param;
70	struct smb_trans2 io;
71};
72
73NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
74			     TALLOC_CTX *mem_ctx,
75			     struct smb_trans2 *parms)
76{
77	struct smb_raw_trans2_recv_state *state;
78
79	if (!smbcli_request_receive(req) ||
80	    smbcli_request_is_error(req)) {
81		goto failed;
82	}
83
84	state = talloc_get_type(req->recv_helper.private_data,
85				struct smb_raw_trans2_recv_state);
86
87	parms->out = state->io.out;
88	talloc_steal(mem_ctx, parms->out.setup);
89	talloc_steal(mem_ctx, parms->out.params.data);
90	talloc_steal(mem_ctx, parms->out.data.data);
91	talloc_free(state);
92
93	ZERO_STRUCT(req->recv_helper);
94
95failed:
96	return smbcli_request_destroy(req);
97}
98
99static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
100							  struct smb_raw_trans2_recv_state *state);
101
102/*
103 * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
104 */
105static enum smbcli_request_state smb_raw_trans2_recv_helper(struct smbcli_request *req)
106{
107	struct smb_raw_trans2_recv_state *state = talloc_get_type(req->recv_helper.private_data,
108						  struct smb_raw_trans2_recv_state);
109	uint16_t param_count, param_ofs, param_disp;
110	uint16_t data_count, data_ofs, data_disp;
111	uint16_t total_data, total_param;
112	uint8_t setup_count;
113
114	/*
115	 * An NT RPC pipe call can return ERRDOS, ERRmoredata
116	 * to a trans call. This is not an error and should not
117	 * be treated as such.
118	 */
119	if (smbcli_request_is_error(req)) {
120		goto failed;
121	}
122
123	if (state->params_left > 0 || state->data_left > 0) {
124		return smb_raw_trans2_ship_rest(req, state);
125	}
126
127	SMBCLI_CHECK_MIN_WCT(req, 10);
128
129	total_data = SVAL(req->in.vwv, VWV(1));
130	total_param = SVAL(req->in.vwv, VWV(0));
131	setup_count = CVAL(req->in.vwv, VWV(9));
132
133	param_count = SVAL(req->in.vwv, VWV(3));
134	param_ofs   = SVAL(req->in.vwv, VWV(4));
135	param_disp  = SVAL(req->in.vwv, VWV(5));
136
137	data_count = SVAL(req->in.vwv, VWV(6));
138	data_ofs   = SVAL(req->in.vwv, VWV(7));
139	data_disp  = SVAL(req->in.vwv, VWV(8));
140
141	if (!state->got_first) {
142		if (total_param > 0) {
143			state->io.out.params = data_blob_talloc(state, NULL, total_param);
144			if (!state->io.out.params.data) {
145				goto nomem;
146			}
147		}
148
149		if (total_data > 0) {
150			state->io.out.data = data_blob_talloc(state, NULL, total_data);
151			if (!state->io.out.data.data) {
152				goto nomem;
153			}
154		}
155
156		if (setup_count > 0) {
157			uint16_t i;
158
159			SMBCLI_CHECK_WCT(req, 10 + setup_count);
160
161			state->io.out.setup_count = setup_count;
162			state->io.out.setup = talloc_array(state, uint16_t, setup_count);
163			if (!state->io.out.setup) {
164				goto nomem;
165			}
166			for (i=0; i < setup_count; i++) {
167				state->io.out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
168			}
169		}
170
171		state->got_first = true;
172	}
173
174	if (total_data > state->io.out.data.length ||
175	    total_param > state->io.out.params.length) {
176		/* they must *only* shrink */
177		DEBUG(1,("smb_raw_trans2_recv_helper: data/params expanded!\n"));
178		req->status = NT_STATUS_BUFFER_TOO_SMALL;
179		goto failed;
180	}
181
182	state->io.out.data.length = total_data;
183	state->io.out.params.length = total_param;
184
185	if (data_count + data_disp > total_data ||
186	    param_count + param_disp > total_param) {
187		DEBUG(1,("smb_raw_trans2_recv_helper: Buffer overflow\n"));
188		req->status = NT_STATUS_BUFFER_TOO_SMALL;
189		goto failed;
190	}
191
192	/* check the server isn't being nasty */
193	if (raw_trans_oob(req, param_ofs, param_count) ||
194	    raw_trans_oob(req, data_ofs, data_count)) {
195		DEBUG(1,("smb_raw_trans2_recv_helper: out of bounds parameters!\n"));
196		req->status = NT_STATUS_BUFFER_TOO_SMALL;
197		goto failed;
198	}
199
200	if (data_count) {
201		memcpy(state->io.out.data.data + data_disp,
202		       req->in.hdr + data_ofs,
203		       data_count);
204	}
205
206	if (param_count) {
207		memcpy(state->io.out.params.data + param_disp,
208		       req->in.hdr + param_ofs,
209		       param_count);
210	}
211
212	state->recvd_param += param_count;
213	state->recvd_data += data_count;
214
215	if (state->recvd_data < total_data ||
216	    state->recvd_param < total_param) {
217
218		/* we don't need the in buffer any more */
219		talloc_free(req->in.buffer);
220		ZERO_STRUCT(req->in);
221
222		/* we still wait for more data */
223		DEBUG(10,("smb_raw_trans2_recv_helper: more data needed\n"));
224		return SMBCLI_REQUEST_RECV;
225	}
226
227	DEBUG(10,("smb_raw_trans2_recv_helper: done\n"));
228	return SMBCLI_REQUEST_DONE;
229
230nomem:
231	req->status = NT_STATUS_NO_MEMORY;
232failed:
233	return SMBCLI_REQUEST_ERROR;
234}
235
236_PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
237			     TALLOC_CTX *mem_ctx,
238			     struct smb_trans2 *parms)
239{
240	return smb_raw_trans2_recv(req, mem_ctx, parms);
241}
242
243
244/*
245  trans/trans2 raw async interface - only BLOBs used in this interface.
246*/
247struct smbcli_request *smb_raw_trans_send_backend(struct smbcli_tree *tree,
248						  struct smb_trans2 *parms,
249						  uint8_t command)
250{
251	struct smb_raw_trans2_recv_state *state;
252	struct smbcli_request *req;
253	int i;
254	int padding;
255	size_t space_left;
256	size_t namelen = 0;
257	DATA_BLOB params_chunk;
258	uint16_t ofs;
259	uint16_t params_ofs = 0;
260	DATA_BLOB data_chunk;
261	uint16_t data_ofs = 0;
262
263	if (parms->in.params.length > UINT16_MAX ||
264	    parms->in.data.length > UINT16_MAX) {
265		DEBUG(3,("Attempt to send invalid trans2 request (params %u, data %u)\n",
266			 (unsigned)parms->in.params.length, (unsigned)parms->in.data.length));
267		return NULL;
268	}
269
270
271	if (command == SMBtrans)
272		padding = 1;
273	else
274		padding = 3;
275
276	req = smbcli_request_setup(tree, command,
277				   14 + parms->in.setup_count,
278				   padding);
279	if (!req) {
280		return NULL;
281	}
282
283	state = talloc_zero(req, struct smb_raw_trans2_recv_state);
284	if (!state) {
285		smbcli_request_destroy(req);
286		return NULL;
287	}
288
289	state->command = command;
290
291	/* make sure we don't leak data via the padding */
292	memset(req->out.data, 0, padding);
293
294	/* Watch out, this changes the req->out.* pointers */
295	if (command == SMBtrans && parms->in.trans_name) {
296		namelen = smbcli_req_append_string(req, parms->in.trans_name,
297						STR_TERMINATE);
298	}
299
300	ofs = PTR_DIFF(req->out.data,req->out.hdr)+padding+namelen;
301
302	/* see how much bytes of the params block we can ship in the first request */
303	space_left = raw_trans_space_left(req);
304
305	params_chunk.length = MIN(parms->in.params.length, space_left);
306	params_chunk.data = parms->in.params.data;
307	params_ofs = ofs;
308
309	state->params_left = parms->in.params.length - params_chunk.length;
310
311	if (state->params_left > 0) {
312		/* we copy the whole params block, if needed we can optimize that latter */
313		state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
314		if (!state->io.in.params.data) {
315			smbcli_request_destroy(req);
316			return NULL;
317		}
318		memcpy(state->io.in.params.data,
319		       parms->in.params.data,
320		       parms->in.params.length);
321	}
322
323	/* see how much bytes of the data block we can ship in the first request */
324	space_left -= params_chunk.length;
325
326#if TORTURE_TRANS_DATA
327	if (space_left > 1) {
328		space_left /= 2;
329	}
330#endif
331
332	data_chunk.length = MIN(parms->in.data.length, space_left);
333	data_chunk.data = parms->in.data.data;
334	data_ofs = params_ofs + params_chunk.length;
335
336	state->data_left = parms->in.data.length - data_chunk.length;
337
338	if (state->data_left > 0) {
339		/* we copy the whole params block, if needed we can optimize that latter */
340		state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
341		if (!state->io.in.data.data) {
342			smbcli_request_destroy(req);
343			return NULL;
344		}
345		memcpy(state->io.in.data.data,
346		       parms->in.data.data,
347		       parms->in.data.length);
348	}
349
350	state->params_total = parms->in.params.length;
351	state->data_total = parms->in.data.length;
352
353	/* primary request */
354	SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
355	SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
356	SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
357	SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
358	SCVAL(req->out.vwv,VWV(4),parms->in.max_setup);
359	SCVAL(req->out.vwv,VWV(4)+1,0); /* reserved */
360	SSVAL(req->out.vwv,VWV(5),parms->in.flags);
361	SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
362	SSVAL(req->out.vwv,VWV(8),0); /* reserved */
363	SSVAL(req->out.vwv,VWV(9),params_chunk.length);
364	SSVAL(req->out.vwv,VWV(10),params_ofs);
365	SSVAL(req->out.vwv,VWV(11),data_chunk.length);
366	SSVAL(req->out.vwv,VWV(12),data_ofs);
367	SCVAL(req->out.vwv,VWV(13),parms->in.setup_count);
368	SCVAL(req->out.vwv,VWV(13)+1,0); /* reserved */
369	for (i=0;i<parms->in.setup_count;i++)	{
370		SSVAL(req->out.vwv,VWV(14)+VWV(i),parms->in.setup[i]);
371	}
372	smbcli_req_append_blob(req, &params_chunk);
373	smbcli_req_append_blob(req, &data_chunk);
374
375	/* add the helper which will check that all multi-part replies are
376	   in before an async client callack will be issued */
377	req->recv_helper.fn = smb_raw_trans2_recv_helper;
378	req->recv_helper.private_data = state;
379
380	if (!smbcli_request_send(req)) {
381		smbcli_request_destroy(req);
382		return NULL;
383	}
384
385	return req;
386}
387
388static enum smbcli_request_state smb_raw_trans2_ship_next(struct smbcli_request *req,
389							  struct smb_raw_trans2_recv_state *state)
390{
391	struct smbcli_request *req2;
392	size_t space_left;
393	DATA_BLOB params_chunk;
394	uint16_t ofs;
395	uint16_t params_ofs = 0;
396	uint16_t params_disp = 0;
397	DATA_BLOB data_chunk;
398	uint16_t data_ofs = 0;
399	uint16_t data_disp = 0;
400	uint8_t wct;
401
402	if (state->command == SMBtrans2) {
403		wct = 9;
404	} else {
405		wct = 8;
406	}
407
408	req2 = smbcli_request_setup(req->tree, state->command+1, wct, 0);
409	if (!req2) {
410		goto nomem;
411	}
412	req2->mid = req->mid;
413	SSVAL(req2->out.hdr, HDR_MID, req2->mid);
414
415	ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
416
417	/* see how much bytes of the params block we can ship in the first request */
418	space_left = raw_trans_space_left(req2);
419
420	params_disp = state->io.in.params.length - state->params_left;
421	params_chunk.length = MIN(state->params_left, space_left);
422	params_chunk.data = state->io.in.params.data + params_disp;
423	params_ofs = ofs;
424
425	state->params_left -= params_chunk.length;
426
427	/* see how much bytes of the data block we can ship in the first request */
428	space_left -= params_chunk.length;
429
430#if TORTURE_TRANS_DATA
431	if (space_left > 1) {
432		space_left /= 2;
433	}
434#endif
435
436	data_disp = state->io.in.data.length - state->data_left;
437	data_chunk.length = MIN(state->data_left, space_left);
438	data_chunk.data = state->io.in.data.data + data_disp;
439	data_ofs = params_ofs+params_chunk.length;
440
441	state->data_left -= data_chunk.length;
442
443	SSVAL(req2->out.vwv,VWV(0), state->params_total);
444	SSVAL(req2->out.vwv,VWV(1), state->data_total);
445	SSVAL(req2->out.vwv,VWV(2), params_chunk.length);
446	SSVAL(req2->out.vwv,VWV(3), params_ofs);
447	SSVAL(req2->out.vwv,VWV(4), params_disp);
448	SSVAL(req2->out.vwv,VWV(5), data_chunk.length);
449	SSVAL(req2->out.vwv,VWV(6), data_ofs);
450	SSVAL(req2->out.vwv,VWV(7), data_disp);
451	if (wct == 9) {
452		SSVAL(req2->out.vwv,VWV(8), 0xFFFF);
453	}
454
455	smbcli_req_append_blob(req2, &params_chunk);
456	smbcli_req_append_blob(req2, &data_chunk);
457
458	/*
459	 * it's a one way request but we need
460	 * the seq_num, so we destroy req2 by hand
461	 */
462	if (!smbcli_request_send(req2)) {
463		goto failed;
464	}
465
466	req->seq_num = req2->seq_num;
467	smbcli_request_destroy(req2);
468
469	return SMBCLI_REQUEST_RECV;
470
471nomem:
472	req->status = NT_STATUS_NO_MEMORY;
473failed:
474	if (req2) {
475		req->status = smbcli_request_destroy(req2);
476	}
477	return SMBCLI_REQUEST_ERROR;
478}
479
480static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
481							  struct smb_raw_trans2_recv_state *state)
482{
483	enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
484
485	while (state->params_left > 0 || state->data_left > 0) {
486		ret = smb_raw_trans2_ship_next(req, state);
487		if (ret != SMBCLI_REQUEST_RECV) {
488			break;
489		}
490	}
491
492	return ret;
493}
494
495
496/*
497  trans/trans2 raw async interface - only BLOBs used in this interface.
498  note that this doesn't yet support multi-part requests
499*/
500_PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree,
501				       struct smb_trans2 *parms)
502{
503	return smb_raw_trans_send_backend(tree, parms, SMBtrans);
504}
505
506struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree,
507				       struct smb_trans2 *parms)
508{
509	return smb_raw_trans_send_backend(tree, parms, SMBtrans2);
510}
511
512/*
513  trans2 synchronous blob interface
514*/
515NTSTATUS smb_raw_trans2(struct smbcli_tree *tree,
516			TALLOC_CTX *mem_ctx,
517			struct smb_trans2 *parms)
518{
519	struct smbcli_request *req;
520	req = smb_raw_trans2_send(tree, parms);
521	if (!req) return NT_STATUS_UNSUCCESSFUL;
522	return smb_raw_trans2_recv(req, mem_ctx, parms);
523}
524
525
526/*
527  trans synchronous blob interface
528*/
529_PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
530		       TALLOC_CTX *mem_ctx,
531		       struct smb_trans2 *parms)
532{
533	struct smbcli_request *req;
534	req = smb_raw_trans_send(tree, parms);
535	if (!req) return NT_STATUS_UNSUCCESSFUL;
536	return smb_raw_trans_recv(req, mem_ctx, parms);
537}
538
539struct smb_raw_nttrans_recv_state {
540	uint32_t params_total;
541	uint32_t data_total;
542	uint32_t params_left;
543	uint32_t data_left;
544	bool got_first;
545	uint32_t recvd_data;
546	uint32_t recvd_param;
547	struct smb_nttrans io;
548};
549
550NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
551			      TALLOC_CTX *mem_ctx,
552			      struct smb_nttrans *parms)
553{
554	struct smb_raw_nttrans_recv_state *state;
555
556	if (!smbcli_request_receive(req) ||
557	    smbcli_request_is_error(req)) {
558		goto failed;
559	}
560
561	state = talloc_get_type(req->recv_helper.private_data,
562				struct smb_raw_nttrans_recv_state);
563
564	parms->out = state->io.out;
565	talloc_steal(mem_ctx, parms->out.setup);
566	talloc_steal(mem_ctx, parms->out.params.data);
567	talloc_steal(mem_ctx, parms->out.data.data);
568	talloc_free(state);
569
570	ZERO_STRUCT(req->recv_helper);
571
572failed:
573	return smbcli_request_destroy(req);
574}
575
576static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
577							   struct smb_raw_nttrans_recv_state *state);
578
579/*
580 * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
581 */
582static enum smbcli_request_state smb_raw_nttrans_recv_helper(struct smbcli_request *req)
583{
584	struct smb_raw_nttrans_recv_state *state = talloc_get_type(req->recv_helper.private_data,
585						   struct smb_raw_nttrans_recv_state);
586	uint32_t param_count, param_ofs, param_disp;
587	uint32_t data_count, data_ofs, data_disp;
588	uint32_t total_data, total_param;
589	uint8_t setup_count;
590
591	/*
592	 * An NT RPC pipe call can return ERRDOS, ERRmoredata
593	 * to a trans call. This is not an error and should not
594	 * be treated as such.
595	 */
596	if (smbcli_request_is_error(req)) {
597		goto failed;
598	}
599
600	/* sanity check */
601	if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
602		DEBUG(0,("smb_raw_nttrans_recv_helper: Expected %s response, got command 0x%02x\n",
603			 "SMBnttrans",
604			 CVAL(req->in.hdr,HDR_COM)));
605		req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
606		goto failed;
607	}
608
609	if (state->params_left > 0 || state->data_left > 0) {
610		return smb_raw_nttrans_ship_rest(req, state);
611	}
612
613	/* this is the first packet of the response */
614	SMBCLI_CHECK_MIN_WCT(req, 18);
615
616	total_param = IVAL(req->in.vwv, 3);
617	total_data  = IVAL(req->in.vwv, 7);
618	setup_count = CVAL(req->in.vwv, 35);
619
620	param_count = IVAL(req->in.vwv, 11);
621	param_ofs   = IVAL(req->in.vwv, 15);
622	param_disp  = IVAL(req->in.vwv, 19);
623
624	data_count = IVAL(req->in.vwv, 23);
625	data_ofs   = IVAL(req->in.vwv, 27);
626	data_disp  = IVAL(req->in.vwv, 31);
627
628	if (!state->got_first) {
629		if (total_param > 0) {
630			state->io.out.params = data_blob_talloc(state, NULL, total_param);
631			if (!state->io.out.params.data) {
632				goto nomem;
633			}
634		}
635
636		if (total_data > 0) {
637			state->io.out.data = data_blob_talloc(state, NULL, total_data);
638			if (!state->io.out.data.data) {
639				goto nomem;
640			}
641		}
642
643		if (setup_count > 0) {
644			SMBCLI_CHECK_WCT(req, 18 + setup_count);
645
646			state->io.out.setup_count = setup_count;
647			state->io.out.setup = talloc_array(state, uint8_t,
648							   setup_count * VWV(1));
649			if (!state->io.out.setup) {
650				goto nomem;
651			}
652			memcpy(state->io.out.setup, (uint8_t *)req->out.vwv + VWV(18),
653			       setup_count * VWV(1));
654		}
655
656		state->got_first = true;
657	}
658
659	if (total_data > state->io.out.data.length ||
660	    total_param > state->io.out.params.length) {
661		/* they must *only* shrink */
662		DEBUG(1,("smb_raw_nttrans_recv_helper: data/params expanded!\n"));
663		req->status = NT_STATUS_BUFFER_TOO_SMALL;
664		goto failed;
665	}
666
667	state->io.out.data.length = total_data;
668	state->io.out.params.length = total_param;
669
670	if (data_count + data_disp > total_data ||
671	    param_count + param_disp > total_param) {
672		DEBUG(1,("smb_raw_nttrans_recv_helper: Buffer overflow\n"));
673		req->status = NT_STATUS_BUFFER_TOO_SMALL;
674		goto failed;
675	}
676
677	/* check the server isn't being nasty */
678	if (raw_trans_oob(req, param_ofs, param_count) ||
679	    raw_trans_oob(req, data_ofs, data_count)) {
680		DEBUG(1,("smb_raw_nttrans_recv_helper: out of bounds parameters!\n"));
681		req->status = NT_STATUS_BUFFER_TOO_SMALL;
682		goto failed;
683	}
684
685	if (data_count) {
686		memcpy(state->io.out.data.data + data_disp,
687		       req->in.hdr + data_ofs,
688		       data_count);
689	}
690
691	if (param_count) {
692		memcpy(state->io.out.params.data + param_disp,
693		       req->in.hdr + param_ofs,
694		       param_count);
695	}
696
697	state->recvd_param += param_count;
698	state->recvd_data += data_count;
699
700	if (state->recvd_data < total_data ||
701	    state->recvd_param < total_param) {
702
703		/* we don't need the in buffer any more */
704		talloc_free(req->in.buffer);
705		ZERO_STRUCT(req->in);
706
707		/* we still wait for more data */
708		DEBUG(10,("smb_raw_nttrans_recv_helper: more data needed\n"));
709		return SMBCLI_REQUEST_RECV;
710	}
711
712	DEBUG(10,("smb_raw_nttrans_recv_helper: done\n"));
713	return SMBCLI_REQUEST_DONE;
714
715nomem:
716	req->status = NT_STATUS_NO_MEMORY;
717failed:
718	return SMBCLI_REQUEST_ERROR;
719}
720
721/****************************************************************************
722 nttrans raw - only BLOBs used in this interface.
723 at the moment we only handle a single primary request
724****************************************************************************/
725struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree,
726					 struct smb_nttrans *parms)
727{
728	struct smbcli_request *req;
729	struct smb_raw_nttrans_recv_state *state;
730	uint32_t ofs;
731	size_t space_left;
732	DATA_BLOB params_chunk;
733	uint32_t params_ofs;
734	DATA_BLOB data_chunk;
735	uint32_t data_ofs;
736	int align = 0;
737
738	/* only align if there are parameters or data */
739	if (parms->in.params.length || parms->in.data.length) {
740		align = 3;
741	}
742
743	req = smbcli_request_setup(tree, SMBnttrans,
744				19 + parms->in.setup_count, align);
745	if (!req) {
746		return NULL;
747	}
748
749	state = talloc_zero(req, struct smb_raw_nttrans_recv_state);
750	if (!state) {
751		talloc_free(req);
752		return NULL;
753	}
754
755	/* fill in SMB parameters */
756
757	if (align != 0) {
758		memset(req->out.data, 0, align);
759	}
760
761	ofs = PTR_DIFF(req->out.data,req->out.hdr)+align;
762
763	/* see how much bytes of the params block we can ship in the first request */
764	space_left = raw_trans_space_left(req);
765
766	params_chunk.length = MIN(parms->in.params.length, space_left);
767	params_chunk.data = parms->in.params.data;
768	params_ofs = ofs;
769
770	state->params_left = parms->in.params.length - params_chunk.length;
771
772	if (state->params_left > 0) {
773		/* we copy the whole params block, if needed we can optimize that latter */
774		state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
775		if (!state->io.in.params.data) {
776			smbcli_request_destroy(req);
777			return NULL;
778		}
779		memcpy(state->io.in.params.data,
780		       parms->in.params.data,
781		       parms->in.params.length);
782	}
783
784	/* see how much bytes of the data block we can ship in the first request */
785	space_left -= params_chunk.length;
786
787#if TORTURE_TRANS_DATA
788	if (space_left > 1) {
789		space_left /= 2;
790	}
791#endif
792
793	data_chunk.length = MIN(parms->in.data.length, space_left);
794	data_chunk.data = parms->in.data.data;
795	data_ofs = params_ofs + params_chunk.length;
796
797	state->data_left = parms->in.data.length - data_chunk.length;
798
799	if (state->data_left > 0) {
800		/* we copy the whole params block, if needed we can optimize that latter */
801		state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
802		if (!state->io.in.data.data) {
803			smbcli_request_destroy(req);
804			return NULL;
805		}
806		memcpy(state->io.in.data.data,
807		       parms->in.data.data,
808		       parms->in.data.length);
809	}
810
811	state->params_total = parms->in.params.length;
812	state->data_total = parms->in.data.length;
813
814	SCVAL(req->out.vwv,  0, parms->in.max_setup);
815	SSVAL(req->out.vwv,  1, 0); /* reserved */
816	SIVAL(req->out.vwv,  3, parms->in.params.length);
817	SIVAL(req->out.vwv,  7, parms->in.data.length);
818	SIVAL(req->out.vwv, 11, parms->in.max_param);
819	SIVAL(req->out.vwv, 15, parms->in.max_data);
820	SIVAL(req->out.vwv, 19, params_chunk.length);
821	SIVAL(req->out.vwv, 23, params_ofs);
822	SIVAL(req->out.vwv, 27, data_chunk.length);
823	SIVAL(req->out.vwv, 31, data_ofs);
824	SCVAL(req->out.vwv, 35, parms->in.setup_count);
825	SSVAL(req->out.vwv, 36, parms->in.function);
826	memcpy(req->out.vwv + VWV(19), parms->in.setup,
827	       sizeof(uint16_t) * parms->in.setup_count);
828
829	smbcli_req_append_blob(req, &params_chunk);
830	smbcli_req_append_blob(req, &data_chunk);
831
832	/* add the helper which will check that all multi-part replies are
833	   in before an async client callack will be issued */
834	req->recv_helper.fn = smb_raw_nttrans_recv_helper;
835	req->recv_helper.private_data = state;
836
837	if (!smbcli_request_send(req)) {
838		smbcli_request_destroy(req);
839		return NULL;
840	}
841
842	return req;
843}
844
845static enum smbcli_request_state smb_raw_nttrans_ship_next(struct smbcli_request *req,
846							   struct smb_raw_nttrans_recv_state *state)
847{
848	struct smbcli_request *req2;
849	size_t space_left;
850	DATA_BLOB params_chunk;
851	uint32_t ofs;
852	uint32_t params_ofs = 0;
853	uint32_t params_disp = 0;
854	DATA_BLOB data_chunk;
855	uint32_t data_ofs = 0;
856	uint32_t data_disp = 0;
857
858	req2 = smbcli_request_setup(req->tree, SMBnttranss, 18, 0);
859	if (!req2) {
860		goto nomem;
861	}
862	req2->mid = req->mid;
863	SSVAL(req2->out.hdr, HDR_MID, req2->mid);
864
865	ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
866
867	/* see how much bytes of the params block we can ship in the first request */
868	space_left = raw_trans_space_left(req2);
869
870	params_disp = state->io.in.params.length - state->params_left;
871	params_chunk.length = MIN(state->params_left, space_left);
872	params_chunk.data = state->io.in.params.data + params_disp;
873	params_ofs = ofs;
874
875	state->params_left -= params_chunk.length;
876
877	/* see how much bytes of the data block we can ship in the first request */
878	space_left -= params_chunk.length;
879
880#if TORTURE_TRANS_DATA
881	if (space_left > 1) {
882		space_left /= 2;
883	}
884#endif
885
886	data_disp = state->io.in.data.length - state->data_left;
887	data_chunk.length = MIN(state->data_left, space_left);
888	data_chunk.data = state->io.in.data.data + data_disp;
889	data_ofs = params_ofs+params_chunk.length;
890
891	state->data_left -= data_chunk.length;
892
893	SSVAL(req2->out.vwv,0, 0); /* reserved */
894	SCVAL(req2->out.vwv,2, 0); /* reserved */
895	SIVAL(req2->out.vwv,3, state->params_total);
896	SIVAL(req2->out.vwv,7, state->data_total);
897	SIVAL(req2->out.vwv,11, params_chunk.length);
898	SIVAL(req2->out.vwv,15, params_ofs);
899	SIVAL(req2->out.vwv,19, params_disp);
900	SIVAL(req2->out.vwv,23, data_chunk.length);
901	SIVAL(req2->out.vwv,27, data_ofs);
902	SIVAL(req2->out.vwv,31, data_disp);
903	SCVAL(req2->out.vwv,35, 0); /* reserved */
904
905	smbcli_req_append_blob(req2, &params_chunk);
906	smbcli_req_append_blob(req2, &data_chunk);
907
908	/*
909	 * it's a one way request but we need
910	 * the seq_num, so we destroy req2 by hand
911	 */
912	if (!smbcli_request_send(req2)) {
913		goto failed;
914	}
915
916	req->seq_num = req2->seq_num;
917	smbcli_request_destroy(req2);
918
919	return SMBCLI_REQUEST_RECV;
920
921nomem:
922	req->status = NT_STATUS_NO_MEMORY;
923failed:
924	if (req2) {
925		req->status = smbcli_request_destroy(req2);
926	}
927	return SMBCLI_REQUEST_ERROR;
928}
929
930static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
931							   struct smb_raw_nttrans_recv_state *state)
932{
933	enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
934
935	while (state->params_left > 0 || state->data_left > 0) {
936		ret = smb_raw_nttrans_ship_next(req, state);
937		if (ret != SMBCLI_REQUEST_RECV) {
938			break;
939		}
940	}
941
942	return ret;
943}
944
945
946/****************************************************************************
947  receive a SMB nttrans response allocating the necessary memory
948  ****************************************************************************/
949NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree,
950			 TALLOC_CTX *mem_ctx,
951			 struct smb_nttrans *parms)
952{
953	struct smbcli_request *req;
954
955	req = smb_raw_nttrans_send(tree, parms);
956	if (!req) {
957		return NT_STATUS_UNSUCCESSFUL;
958	}
959
960	return smb_raw_nttrans_recv(req, mem_ctx, parms);
961}
962