2 Unix SMB/CIFS implementation.
4 SMB2 client transport context management functions
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "system/network.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "libcli/smb2/smb2.h"
27 #include "libcli/smb2/smb2_calls.h"
28 #include "lib/socket/socket.h"
29 #include "lib/events/events.h"
30 #include "../lib/util/dlinklist.h"
31 #include "../libcli/smb/smbXcli_base.h"
32 #include "librpc/ndr/libndr.h"
37 static int transport_destructor(struct smb2_transport
*transport
)
39 smb2_transport_dead(transport
, NT_STATUS_LOCAL_DISCONNECT
);
44 create a transport structure based on an established socket
46 struct smb2_transport
*smb2_transport_init(struct smbcli_socket
*sock
,
47 TALLOC_CTX
*parent_ctx
,
48 struct smbcli_options
*options
)
50 struct smb2_transport
*transport
;
52 transport
= talloc_zero(parent_ctx
, struct smb2_transport
);
53 if (!transport
) return NULL
;
55 transport
->ev
= sock
->event
.ctx
;
56 transport
->options
= *options
;
58 if (transport
->options
.max_protocol
== PROTOCOL_DEFAULT
) {
59 transport
->options
.max_protocol
= PROTOCOL_LATEST
;
62 if (transport
->options
.max_protocol
< PROTOCOL_SMB2_02
) {
63 transport
->options
.max_protocol
= PROTOCOL_LATEST
;
66 TALLOC_FREE(sock
->event
.fde
);
67 TALLOC_FREE(sock
->event
.te
);
69 transport
->conn
= smbXcli_conn_create(transport
,
73 0, /* smb1_capabilities */
74 &options
->client_guid
,
75 options
->smb2_capabilities
,
76 &options
->smb3_capabilities
);
77 if (transport
->conn
== NULL
) {
78 talloc_free(transport
);
84 talloc_set_destructor(transport
, transport_destructor
);
90 create a transport structure based on an established socket
92 NTSTATUS
smb2_transport_raw_init(TALLOC_CTX
*mem_ctx
,
93 struct tevent_context
*ev
,
94 struct smbXcli_conn
**_conn
,
95 const struct smbcli_options
*options
,
96 struct smb2_transport
**_transport
)
98 struct smb2_transport
*transport
= NULL
;
99 enum protocol_types protocol
;
101 if (*_conn
== NULL
) {
102 return NT_STATUS_INVALID_PARAMETER
;
105 protocol
= smbXcli_conn_protocol(*_conn
);
106 if (protocol
< PROTOCOL_SMB2_02
) {
107 return NT_STATUS_REVISION_MISMATCH
;
110 transport
= talloc_zero(mem_ctx
, struct smb2_transport
);
111 if (transport
== NULL
) {
112 return NT_STATUS_NO_MEMORY
;
116 transport
->options
= *options
;
117 transport
->conn
= talloc_move(transport
, _conn
);
119 talloc_set_destructor(transport
, transport_destructor
);
120 *_transport
= transport
;
125 mark the transport as dead
127 void smb2_transport_dead(struct smb2_transport
*transport
, NTSTATUS status
)
129 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL
, status
)) {
130 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
132 if (NT_STATUS_IS_OK(status
)) {
133 status
= NT_STATUS_LOCAL_DISCONNECT
;
136 smbXcli_conn_disconnect(transport
->conn
, status
);
139 static void smb2_request_done(struct tevent_req
*subreq
);
140 static void smb2_transport_break_handler(struct tevent_req
*subreq
);
143 put a request into the send queue
145 void smb2_transport_send(struct smb2_request
*req
)
148 struct smb2_transport
*transport
= req
->transport
;
149 struct tevent_req
**reqs
= transport
->compound
.reqs
;
150 size_t num_reqs
= talloc_array_length(reqs
);
152 uint16_t cmd
= SVAL(req
->out
.hdr
, SMB2_HDR_OPCODE
);
153 uint32_t additional_flags
= IVAL(req
->out
.hdr
, SMB2_HDR_FLAGS
);
154 uint32_t clear_flags
= 0;
155 struct smbXcli_tcon
*tcon
= NULL
;
156 struct smbXcli_session
*session
= NULL
;
157 bool need_pending_break
= false;
160 DATA_BLOB body
= data_blob_null
;
161 DATA_BLOB dyn
= data_blob_null
;
162 uint32_t timeout_msec
= transport
->options
.request_timeout
* 1000;
164 if (transport
->oplock
.handler
) {
165 need_pending_break
= true;
168 if (transport
->lease
.handler
) {
169 need_pending_break
= true;
172 if (transport
->break_subreq
) {
173 need_pending_break
= false;
176 if (need_pending_break
) {
177 struct tevent_req
*subreq
;
179 subreq
= smb2cli_req_create(transport
,
183 0, /* additional_flags */
185 0, /* timeout_msec */
192 0); /* max_dyn_len */
193 if (subreq
!= NULL
) {
194 smbXcli_req_set_pending(subreq
);
195 tevent_req_set_callback(subreq
,
196 smb2_transport_break_handler
,
198 transport
->break_subreq
= subreq
;
203 session
= req
->session
->smbXcli
;
207 tcon
= req
->tree
->smbXcli
;
210 if (transport
->compound
.related
) {
211 additional_flags
|= SMB2_HDR_FLAG_CHAINED
;
214 hdr_ofs
= PTR_DIFF(req
->out
.hdr
, req
->out
.buffer
);
215 pdu_len
= req
->out
.size
- hdr_ofs
;
216 body
.data
= req
->out
.body
;
217 body
.length
= req
->out
.body_fixed
;
218 dyn
.data
= req
->out
.body
+ req
->out
.body_fixed
;
219 dyn
.length
= pdu_len
- (SMB2_HDR_BODY
+ req
->out
.body_fixed
);
221 req
->subreq
= smb2cli_req_create(req
,
230 body
.data
, body
.length
,
231 dyn
.data
, dyn
.length
,
232 0); /* max_dyn_len */
233 if (req
->subreq
== NULL
) {
234 req
->state
= SMB2_REQUEST_ERROR
;
235 req
->status
= NT_STATUS_NO_MEMORY
;
239 if (!tevent_req_is_in_progress(req
->subreq
)) {
240 req
->state
= SMB2_REQUEST_ERROR
;
241 req
->status
= NT_STATUS_INTERNAL_ERROR
;/* TODO */
245 tevent_req_set_callback(req
->subreq
, smb2_request_done
, req
);
247 smb2cli_req_set_notify_async(req
->subreq
);
248 if (req
->credit_charge
) {
249 smb2cli_req_set_credit_charge(req
->subreq
, req
->credit_charge
);
252 ZERO_STRUCT(req
->out
);
253 req
->state
= SMB2_REQUEST_RECV
;
256 for (i
=0; i
< num_reqs
; i
++) {
257 if (reqs
[i
] != NULL
) {
261 reqs
[i
] = req
->subreq
;
273 status
= smb2cli_req_compound_submit(reqs
, num_reqs
);
275 TALLOC_FREE(transport
->compound
.reqs
);
276 transport
->compound
.related
= false;
278 if (!NT_STATUS_IS_OK(status
)) {
279 req
->status
= status
;
280 req
->state
= SMB2_REQUEST_ERROR
;
281 smbXcli_conn_disconnect(transport
->conn
, status
);
285 static void smb2_request_done(struct tevent_req
*subreq
)
287 struct smb2_request
*req
=
288 tevent_req_callback_data(subreq
,
289 struct smb2_request
);
293 req
->recv_iov
= NULL
;
295 req
->status
= smb2cli_req_recv(req
->subreq
, req
, &req
->recv_iov
, NULL
, 0);
296 if (NT_STATUS_EQUAL(req
->status
, NT_STATUS_PENDING
)) {
297 struct timeval endtime
= smbXcli_req_endtime(subreq
);
300 req
->cancel
.can_cancel
= true;
301 if (timeval_is_zero(&endtime
)) {
305 ok
= tevent_req_set_endtime(
306 subreq
, req
->transport
->ev
, endtime
);
308 req
->status
= NT_STATUS_INTERNAL_ERROR
;
309 req
->state
= SMB2_REQUEST_ERROR
;
317 TALLOC_FREE(req
->subreq
);
318 if (!NT_STATUS_IS_OK(req
->status
)) {
319 if (req
->recv_iov
== NULL
) {
320 req
->state
= SMB2_REQUEST_ERROR
;
328 len
= req
->recv_iov
[0].iov_len
;
329 for (i
=1; i
< 3; i
++) {
330 uint8_t *p
= req
->recv_iov
[i
-1].iov_base
;
331 uint8_t *c1
= req
->recv_iov
[i
].iov_base
;
332 uint8_t *c2
= p
+ req
->recv_iov
[i
-1].iov_len
;
334 len
+= req
->recv_iov
[i
].iov_len
;
336 if (req
->recv_iov
[i
].iov_len
== 0) {
341 req
->status
= NT_STATUS_INTERNAL_ERROR
;
342 req
->state
= SMB2_REQUEST_ERROR
;
350 req
->in
.buffer
= req
->recv_iov
[0].iov_base
;
352 req
->in
.allocated
= req
->in
.size
;
354 req
->in
.hdr
= req
->recv_iov
[0].iov_base
;
355 req
->in
.body
= req
->recv_iov
[1].iov_base
;
356 req
->in
.dynamic
= req
->recv_iov
[2].iov_base
;
357 req
->in
.body_fixed
= req
->recv_iov
[1].iov_len
;
358 req
->in
.body_size
= req
->in
.body_fixed
;
359 req
->in
.body_size
+= req
->recv_iov
[2].iov_len
;
361 smb2_setup_bufinfo(req
);
363 req
->state
= SMB2_REQUEST_DONE
;
369 static void smb2_transport_break_handler(struct tevent_req
*subreq
)
371 struct smb2_transport
*transport
=
372 tevent_req_callback_data(subreq
,
373 struct smb2_transport
);
378 struct iovec
*recv_iov
= NULL
;
380 transport
->break_subreq
= NULL
;
382 status
= smb2cli_req_recv(subreq
, transport
, &recv_iov
, NULL
, 0);
384 if (!NT_STATUS_IS_OK(status
)) {
385 TALLOC_FREE(recv_iov
);
386 smb2_transport_dead(transport
, status
);
391 * Setup the subreq to handle the
392 * next incoming SMB2 Break.
394 subreq
= smb2cli_req_create(transport
,
398 0, /* additional_flags */
400 0, /* timeout_msec */
407 0); /* max_dyn_len */
408 if (subreq
!= NULL
) {
409 smbXcli_req_set_pending(subreq
);
410 tevent_req_set_callback(subreq
,
411 smb2_transport_break_handler
,
413 transport
->break_subreq
= subreq
;
416 body
= recv_iov
[1].iov_base
;
418 len
= recv_iov
[1].iov_len
;
419 if (recv_iov
[1].iov_len
>= 2) {
420 len
= CVAL(body
, 0x00);
421 if (len
!= recv_iov
[1].iov_len
) {
422 len
= recv_iov
[1].iov_len
;
428 } else if (len
== 44) {
431 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
433 TALLOC_FREE(recv_iov
);
434 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
435 smb2_transport_dead(transport
, status
);
439 if (!lease
&& transport
->oplock
.handler
) {
440 struct smb2_handle h
;
443 level
= CVAL(body
, 0x02);
444 smb2_pull_handle(body
+0x08, &h
);
446 TALLOC_FREE(recv_iov
);
448 transport
->oplock
.handler(transport
, &h
, level
,
449 transport
->oplock
.private_data
);
450 } else if (lease
&& transport
->lease
.handler
) {
451 struct smb2_lease_break lb
;
454 lb
.new_epoch
= SVAL(body
, 0x2);
455 lb
.break_flags
= SVAL(body
, 0x4);
456 memcpy(&lb
.current_lease
.lease_key
, body
+0x8,
457 sizeof(struct smb2_lease_key
));
458 lb
.current_lease
.lease_state
= SVAL(body
, 0x18);
459 lb
.new_lease_state
= SVAL(body
, 0x1C);
460 lb
.break_reason
= SVAL(body
, 0x20);
461 lb
.access_mask_hint
= SVAL(body
, 0x24);
462 lb
.share_mask_hint
= SVAL(body
, 0x28);
464 TALLOC_FREE(recv_iov
);
466 transport
->lease
.handler(transport
, &lb
,
467 transport
->lease
.private_data
);
469 DEBUG(5,("Got SMB2 %s break with no handler\n",
470 lease
? "lease" : "oplock"));
472 TALLOC_FREE(recv_iov
);
475 NTSTATUS
smb2_transport_compound_start(struct smb2_transport
*transport
,
478 TALLOC_FREE(transport
->compound
.reqs
);
479 ZERO_STRUCT(transport
->compound
);
481 transport
->compound
.reqs
= talloc_zero_array(transport
,
484 if (transport
->compound
.reqs
== NULL
) {
485 return NT_STATUS_NO_MEMORY
;
491 void smb2_transport_compound_set_related(struct smb2_transport
*transport
,
494 transport
->compound
.related
= related
;
497 void smb2_transport_credits_ask_num(struct smb2_transport
*transport
,
500 smb2cli_conn_set_max_credits(transport
->conn
, ask_num
);
503 static void idle_handler(struct tevent_context
*ev
,
504 struct tevent_timer
*te
, struct timeval t
, void *private_data
)
506 struct smb2_transport
*transport
= talloc_get_type(private_data
,
507 struct smb2_transport
);
510 transport
->idle
.func(transport
, transport
->idle
.private_data
);
512 if (transport
->idle
.func
== NULL
) {
516 if (!smbXcli_conn_is_connected(transport
->conn
)) {
520 next
= timeval_current_ofs_usec(transport
->idle
.period
);
521 transport
->idle
.te
= tevent_add_timer(transport
->ev
,
529 setup the idle handler for a transport
530 the period is in microseconds
532 void smb2_transport_idle_handler(struct smb2_transport
*transport
,
533 void (*idle_func
)(struct smb2_transport
*, void *),
537 TALLOC_FREE(transport
->idle
.te
);
538 ZERO_STRUCT(transport
->idle
);
540 if (idle_func
== NULL
) {
544 if (!smbXcli_conn_is_connected(transport
->conn
)) {
548 transport
->idle
.func
= idle_func
;
549 transport
->idle
.private_data
= private_data
;
550 transport
->idle
.period
= period
;
552 transport
->idle
.te
= tevent_add_timer(transport
->ev
,
554 timeval_current_ofs_usec(period
),