2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "async_smb.h"
23 #include "../libcli/smb/smbXcli_base.h"
25 #include "libsmb/proto.h"
26 #include "lib/util/tevent_ntstatus.h"
27 #include "../librpc/ndr/libndr.h"
29 struct smb2cli_ioctl_state
{
32 uint32_t max_input_length
;
33 uint32_t max_output_length
;
34 struct iovec
*recv_iov
;
35 DATA_BLOB out_input_buffer
;
36 DATA_BLOB out_output_buffer
;
39 static void smb2cli_ioctl_done(struct tevent_req
*subreq
);
41 struct tevent_req
*smb2cli_ioctl_send(TALLOC_CTX
*mem_ctx
,
42 struct tevent_context
*ev
,
43 struct smbXcli_conn
*conn
,
44 uint32_t timeout_msec
,
45 struct smbXcli_session
*session
,
47 uint64_t in_fid_persistent
,
48 uint64_t in_fid_volatile
,
50 uint32_t in_max_input_length
,
51 const DATA_BLOB
*in_input_buffer
,
52 uint32_t in_max_output_length
,
53 const DATA_BLOB
*in_output_buffer
,
56 struct tevent_req
*req
, *subreq
;
57 struct smb2cli_ioctl_state
*state
;
61 uint32_t input_buffer_offset
= 0;
62 uint32_t input_buffer_length
= 0;
63 uint32_t output_buffer_offset
= 0;
64 uint32_t output_buffer_length
= 0;
65 uint32_t pad_length
= 0;
67 req
= tevent_req_create(mem_ctx
, &state
,
68 struct smb2cli_ioctl_state
);
72 state
->max_input_length
= in_max_input_length
;
73 state
->max_output_length
= in_max_output_length
;
75 if (in_input_buffer
) {
76 input_buffer_offset
= SMB2_HDR_BODY
+0x38;
77 input_buffer_length
= in_input_buffer
->length
;
80 if (in_output_buffer
) {
81 output_buffer_offset
= SMB2_HDR_BODY
+0x38;
82 if (input_buffer_length
> 0) {
84 output_buffer_offset
+= input_buffer_length
;
85 tmp
= output_buffer_offset
;
86 output_buffer_offset
= NDR_ROUND(output_buffer_offset
, 8);
87 pad_length
= output_buffer_offset
- tmp
;
89 output_buffer_length
= in_output_buffer
->length
;
94 SSVAL(fixed
, 0x00, 0x39);
95 SSVAL(fixed
, 0x02, 0); /* reserved */
96 SIVAL(fixed
, 0x04, in_ctl_code
);
97 SBVAL(fixed
, 0x08, in_fid_persistent
);
98 SBVAL(fixed
, 0x10, in_fid_volatile
);
99 SIVAL(fixed
, 0x18, input_buffer_offset
);
100 SIVAL(fixed
, 0x1C, input_buffer_length
);
101 SIVAL(fixed
, 0x20, in_max_input_length
);
102 SIVAL(fixed
, 0x24, output_buffer_offset
);
103 SIVAL(fixed
, 0x28, output_buffer_length
);
104 SIVAL(fixed
, 0x2C, in_max_output_length
);
105 SIVAL(fixed
, 0x30, in_flags
);
106 SIVAL(fixed
, 0x34, 0); /* reserved */
108 if (input_buffer_length
> 0 && output_buffer_length
> 0) {
109 size_t avail
= UINT32_MAX
- (input_buffer_length
+ pad_length
);
110 size_t ofs
= output_buffer_offset
- input_buffer_offset
;
112 if (avail
< output_buffer_length
) {
113 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER_MIX
);
114 return tevent_req_post(req
, ev
);
117 dyn_len
= input_buffer_length
+ output_buffer_length
+ pad_length
;
119 dyn
= talloc_zero_array(state
, uint8_t, dyn_len
);
120 if (tevent_req_nomem(dyn
, req
)) {
121 return tevent_req_post(req
, ev
);
123 memcpy(dyn
, in_input_buffer
->data
,
124 in_input_buffer
->length
);
125 memcpy(dyn
+ ofs
, in_output_buffer
->data
,
126 in_output_buffer
->length
);
127 } else if (input_buffer_length
> 0) {
128 dyn
= in_input_buffer
->data
;
129 dyn_len
= in_input_buffer
->length
;
130 } else if (output_buffer_length
> 0) {
131 dyn
= in_output_buffer
->data
;
132 dyn_len
= in_output_buffer
->length
;
134 dyn
= state
->dyn_pad
;
135 dyn_len
= sizeof(state
->dyn_pad
);
138 subreq
= smb2cli_req_send(state
, ev
, conn
, SMB2_OP_IOCTL
,
144 state
->fixed
, sizeof(state
->fixed
),
146 if (tevent_req_nomem(subreq
, req
)) {
147 return tevent_req_post(req
, ev
);
149 tevent_req_set_callback(subreq
, smb2cli_ioctl_done
, req
);
153 static void smb2cli_ioctl_done(struct tevent_req
*subreq
)
155 struct tevent_req
*req
=
156 tevent_req_callback_data(subreq
,
158 struct smb2cli_ioctl_state
*state
=
160 struct smb2cli_ioctl_state
);
166 uint32_t dyn_ofs
= SMB2_HDR_BODY
+ 0x30;
167 uint32_t input_buffer_offset
;
168 uint32_t input_buffer_length
;
169 uint32_t output_buffer_offset
;
170 uint32_t output_buffer_length
;
171 static const struct smb2cli_req_expected_response expected
[] = {
173 .status
= NT_STATUS_OK
,
177 .status
= STATUS_BUFFER_OVERFLOW
,
182 status
= smb2cli_req_recv(subreq
, state
, &iov
,
183 expected
, ARRAY_SIZE(expected
));
184 if (tevent_req_nterror(req
, status
)) {
188 state
->recv_iov
= iov
;
189 fixed
= (uint8_t *)iov
[1].iov_base
;
190 dyn
= (uint8_t *)iov
[2].iov_base
;
191 dyn_len
= iov
[2].iov_len
;
193 input_buffer_offset
= IVAL(fixed
, 0x18);
194 input_buffer_length
= IVAL(fixed
, 0x1C);
195 output_buffer_offset
= IVAL(fixed
, 0x20);
196 output_buffer_length
= IVAL(fixed
, 0x24);
198 if ((input_buffer_offset
> 0) && (input_buffer_length
> 0)) {
201 if (input_buffer_offset
!= dyn_ofs
) {
203 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
207 if (input_buffer_length
< dyn_len
) {
209 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
213 if (input_buffer_length
> state
->max_input_length
) {
215 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
219 state
->out_input_buffer
.data
= dyn
;
220 state
->out_input_buffer
.length
= input_buffer_length
;
222 ofs
= input_buffer_length
;
223 ofs
= NDR_ROUND(ofs
, 8);
230 if ((output_buffer_offset
> 0) && (output_buffer_length
> 0)) {
231 if (output_buffer_offset
!= dyn_ofs
) {
233 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
237 if (output_buffer_length
< dyn_len
) {
239 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
243 if (output_buffer_length
> state
->max_output_length
) {
245 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
249 state
->out_output_buffer
.data
= dyn
;
250 state
->out_output_buffer
.length
= output_buffer_length
;
253 tevent_req_done(req
);
256 NTSTATUS
smb2cli_ioctl_recv(struct tevent_req
*req
,
258 DATA_BLOB
*out_input_buffer
,
259 DATA_BLOB
*out_output_buffer
)
261 struct smb2cli_ioctl_state
*state
=
263 struct smb2cli_ioctl_state
);
266 if (tevent_req_is_nterror(req
, &status
)) {
267 tevent_req_received(req
);
271 talloc_steal(mem_ctx
, state
->recv_iov
);
272 if (out_input_buffer
) {
273 *out_input_buffer
= state
->out_input_buffer
;
275 if (out_output_buffer
) {
276 *out_output_buffer
= state
->out_output_buffer
;
279 tevent_req_received(req
);
283 NTSTATUS
smb2cli_ioctl(struct smbXcli_conn
*conn
,
284 uint32_t timeout_msec
,
285 struct smbXcli_session
*session
,
287 uint64_t in_fid_persistent
,
288 uint64_t in_fid_volatile
,
289 uint32_t in_ctl_code
,
290 uint32_t in_max_input_length
,
291 const DATA_BLOB
*in_input_buffer
,
292 uint32_t in_max_output_length
,
293 const DATA_BLOB
*in_output_buffer
,
296 DATA_BLOB
*out_input_buffer
,
297 DATA_BLOB
*out_output_buffer
)
299 TALLOC_CTX
*frame
= talloc_stackframe();
300 struct tevent_context
*ev
;
301 struct tevent_req
*req
;
302 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
304 if (smbXcli_conn_has_async_calls(conn
)) {
306 * Can't use sync call while an async call is in flight
308 status
= NT_STATUS_INVALID_PARAMETER_MIX
;
311 ev
= tevent_context_init(frame
);
315 req
= smb2cli_ioctl_send(frame
, ev
, conn
, timeout_msec
,
322 in_max_output_length
,
328 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
331 status
= smb2cli_ioctl_recv(req
, mem_ctx
,