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 "libcli/raw/libcliraw.h"
24 #include "libcli/raw/raw_proto.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "lib/socket/socket.h"
28 #include "lib/events/events.h"
29 #include "lib/stream/packet.h"
30 #include "../lib/util/dlinklist.h"
34 an event has happened on the socket
36 static void smb2_transport_event_handler(struct tevent_context
*ev
,
37 struct tevent_fd
*fde
,
38 uint16_t flags
, void *private_data
)
40 struct smb2_transport
*transport
= talloc_get_type(private_data
,
41 struct smb2_transport
);
42 if (flags
& TEVENT_FD_READ
) {
43 packet_recv(transport
->packet
);
46 if (flags
& TEVENT_FD_WRITE
) {
47 packet_queue_run(transport
->packet
);
54 static int transport_destructor(struct smb2_transport
*transport
)
56 smb2_transport_dead(transport
, NT_STATUS_LOCAL_DISCONNECT
);
64 static void smb2_transport_error(void *private_data
, NTSTATUS status
)
66 struct smb2_transport
*transport
= talloc_get_type(private_data
,
67 struct smb2_transport
);
68 smb2_transport_dead(transport
, status
);
71 static NTSTATUS
smb2_transport_finish_recv(void *private_data
, DATA_BLOB blob
);
74 create a transport structure based on an established socket
76 struct smb2_transport
*smb2_transport_init(struct smbcli_socket
*sock
,
77 TALLOC_CTX
*parent_ctx
,
78 struct smbcli_options
*options
)
80 struct smb2_transport
*transport
;
82 transport
= talloc_zero(parent_ctx
, struct smb2_transport
);
83 if (!transport
) return NULL
;
85 transport
->socket
= talloc_steal(transport
, sock
);
86 transport
->options
= *options
;
87 transport
->credits
.charge
= 0;
88 transport
->credits
.ask_num
= 1;
90 /* setup the stream -> packet parser */
91 transport
->packet
= packet_init(transport
);
92 if (transport
->packet
== NULL
) {
93 talloc_free(transport
);
96 packet_set_private(transport
->packet
, transport
);
97 packet_set_socket(transport
->packet
, transport
->socket
->sock
);
98 packet_set_callback(transport
->packet
, smb2_transport_finish_recv
);
99 packet_set_full_request(transport
->packet
, packet_full_request_nbt
);
100 packet_set_error_handler(transport
->packet
, smb2_transport_error
);
101 packet_set_event_context(transport
->packet
, transport
->socket
->event
.ctx
);
102 packet_set_nofree(transport
->packet
);
104 /* take over event handling from the socket layer - it only
105 handles events up until we are connected */
106 talloc_free(transport
->socket
->event
.fde
);
107 transport
->socket
->event
.fde
= tevent_add_fd(transport
->socket
->event
.ctx
,
109 socket_get_fd(transport
->socket
->sock
),
111 smb2_transport_event_handler
,
114 packet_set_fde(transport
->packet
, transport
->socket
->event
.fde
);
115 packet_set_serialise(transport
->packet
);
117 talloc_set_destructor(transport
, transport_destructor
);
123 mark the transport as dead
125 void smb2_transport_dead(struct smb2_transport
*transport
, NTSTATUS status
)
127 smbcli_sock_dead(transport
->socket
);
129 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL
, status
)) {
130 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
133 /* kill all pending receives */
134 while (transport
->pending_recv
) {
135 struct smb2_request
*req
= transport
->pending_recv
;
136 req
->state
= SMB2_REQUEST_ERROR
;
137 req
->status
= status
;
138 DLIST_REMOVE(transport
->pending_recv
, req
);
145 static NTSTATUS
smb2_handle_oplock_break(struct smb2_transport
*transport
,
146 const DATA_BLOB
*blob
)
150 uint16_t len
, bloblen
;
153 hdr
= blob
->data
+NBT_HDR_SIZE
;
154 body
= hdr
+SMB2_HDR_BODY
;
155 bloblen
= blob
->length
- SMB2_HDR_BODY
;
158 DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
159 (unsigned)blob
->length
));
160 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
163 len
= CVAL(body
, 0x00);
165 DEBUG(1,("Discarding smb2 oplock reply,"
166 "packet claims %u byte body, only %u bytes seen\n",
168 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
173 } else if (len
== 44) {
176 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
177 (unsigned)blob
->length
));
178 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
181 if (!lease
&& transport
->oplock
.handler
) {
182 struct smb2_handle h
;
185 level
= CVAL(body
, 0x02);
186 smb2_pull_handle(body
+0x08, &h
);
188 transport
->oplock
.handler(transport
, &h
, level
,
189 transport
->oplock
.private_data
);
190 } else if (lease
&& transport
->lease
.handler
) {
191 struct smb2_lease_break lb
;
194 lb
.break_flags
= SVAL(body
, 0x4);
195 memcpy(&lb
.current_lease
.lease_key
, body
+0x8,
196 sizeof(struct smb2_lease_key
));
197 lb
.current_lease
.lease_state
= SVAL(body
, 0x18);
198 lb
.new_lease_state
= SVAL(body
, 0x1C);
199 lb
.break_reason
= SVAL(body
, 0x20);
200 lb
.access_mask_hint
= SVAL(body
, 0x24);
201 lb
.share_mask_hint
= SVAL(body
, 0x28);
203 transport
->lease
.handler(transport
, &lb
,
204 transport
->lease
.private_data
);
206 DEBUG(5,("Got SMB2 %s break with no handler\n",
207 lease
? "lease" : "oplock"));
213 struct smb2_transport_compount_response_state
{
214 struct smb2_transport
*transport
;
218 static void smb2_transport_compound_response_handler(struct tevent_context
*ctx
,
219 struct tevent_immediate
*im
,
222 struct smb2_transport_compount_response_state
*state
=
223 talloc_get_type_abort(private_data
,
224 struct smb2_transport_compount_response_state
);
225 struct smb2_transport
*transport
= state
->transport
;
228 status
= smb2_transport_finish_recv(transport
, state
->blob
);
230 if (!NT_STATUS_IS_OK(status
)) {
231 smb2_transport_error(transport
, status
);
236 we have a full request in our receive buffer - match it to a pending request
239 static NTSTATUS
smb2_transport_finish_recv(void *private_data
, DATA_BLOB blob
)
241 struct smb2_transport
*transport
= talloc_get_type(private_data
,
242 struct smb2_transport
);
243 uint8_t *buffer
, *hdr
;
245 struct smb2_request
*req
= NULL
;
248 uint16_t buffer_code
;
249 uint32_t dynamic_size
;
258 hdr
= buffer
+NBT_HDR_SIZE
;
260 if (len
< SMB2_MIN_SIZE
) {
261 DEBUG(1,("Discarding smb2 reply of size %d\n", len
));
265 flags
= IVAL(hdr
, SMB2_HDR_FLAGS
);
266 seqnum
= BVAL(hdr
, SMB2_HDR_MESSAGE_ID
);
267 opcode
= SVAL(hdr
, SMB2_HDR_OPCODE
);
269 /* see MS-SMB2 3.2.5.19 */
270 if (seqnum
== UINT64_MAX
) {
271 if (opcode
!= SMB2_OP_BREAK
) {
272 DEBUG(1,("Discarding packet with invalid seqnum, "
273 "opcode %u\n", opcode
));
274 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
277 return smb2_handle_oplock_break(transport
, &blob
);
280 if (opcode
== SMB2_OP_CANCEL
) {
282 * ignore responses to cancel requests,
283 * this can happen if signing was wrong or
284 * we specified the wrong session id
290 /* match the incoming request against the list of pending requests */
291 for (req
=transport
->pending_recv
; req
; req
=req
->next
) {
292 if (req
->seqnum
== seqnum
) break;
296 DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
297 (long long)seqnum
, SVAL(hdr
, SMB2_HDR_OPCODE
)));
301 /* fill in the 'in' portion of the matching request */
302 req
->in
.buffer
= buffer
;
303 talloc_steal(req
, buffer
);
305 req
->in
.allocated
= req
->in
.size
;
308 req
->in
.body
= hdr
+SMB2_HDR_BODY
;
309 req
->in
.body_size
= req
->in
.size
- (SMB2_HDR_BODY
+NBT_HDR_SIZE
);
310 req
->status
= NT_STATUS(IVAL(hdr
, SMB2_HDR_STATUS
));
312 if ((flags
& SMB2_HDR_FLAG_ASYNC
) &&
313 NT_STATUS_EQUAL(req
->status
, STATUS_PENDING
)) {
314 req
->cancel
.can_cancel
= true;
315 req
->cancel
.async_id
= BVAL(hdr
, SMB2_HDR_ASYNC_ID
);
316 for (i
=0; i
< req
->cancel
.do_cancel
; i
++) {
323 next_ofs
= IVAL(req
->in
.hdr
, SMB2_HDR_NEXT_COMMAND
);
325 if (smb2_oob(&req
->in
, req
->in
.hdr
+ next_ofs
, SMB2_HDR_BODY
+ 2)) {
326 DEBUG(1,("SMB2 request invalid next offset 0x%x\n",
331 req
->in
.size
= NBT_HDR_SIZE
+ next_ofs
;
332 req
->in
.body_size
= req
->in
.size
- (SMB2_HDR_BODY
+NBT_HDR_SIZE
);
335 if (req
->session
&& req
->session
->signing_active
&&
336 !NT_STATUS_EQUAL(req
->status
, NT_STATUS_USER_SESSION_DELETED
)) {
337 status
= smb2_check_signature(&req
->in
,
338 req
->session
->session_key
);
339 if (!NT_STATUS_IS_OK(status
)) {
340 /* the spec says to ignore packets with a bad signature */
346 buffer_code
= SVAL(req
->in
.body
, 0);
347 req
->in
.body_fixed
= (buffer_code
& ~1);
348 req
->in
.dynamic
= NULL
;
349 dynamic_size
= req
->in
.body_size
- req
->in
.body_fixed
;
350 if (dynamic_size
!= 0 && (buffer_code
& 1)) {
351 req
->in
.dynamic
= req
->in
.body
+ req
->in
.body_fixed
;
352 if (smb2_oob(&req
->in
, req
->in
.dynamic
, dynamic_size
)) {
353 DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
359 smb2_setup_bufinfo(req
);
361 DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req
->seqnum
));
362 dump_data(5, req
->in
.body
, req
->in
.body_size
);
365 struct tevent_immediate
*im
;
366 struct smb2_transport_compount_response_state
*state
;
368 state
= talloc(transport
,
369 struct smb2_transport_compount_response_state
);
373 state
->transport
= transport
;
375 state
->blob
= data_blob_talloc(state
, NULL
,
376 blob
.length
- next_ofs
);
377 if (!state
->blob
.data
) {
380 im
= tevent_create_immediate(state
);
385 _smb_setlen_tcp(state
->blob
.data
, state
->blob
.length
- NBT_HDR_SIZE
);
386 memcpy(state
->blob
.data
+ NBT_HDR_SIZE
,
387 req
->in
.hdr
+ next_ofs
,
388 req
->in
.allocated
- req
->in
.size
);
389 tevent_schedule_immediate(im
, transport
->socket
->event
.ctx
,
390 smb2_transport_compound_response_handler
,
394 /* if this request has an async handler then call that to
395 notify that the reply has been received. This might destroy
396 the request so it must happen last */
397 DLIST_REMOVE(transport
->pending_recv
, req
);
398 req
->state
= SMB2_REQUEST_DONE
;
405 dump_data(5, buffer
, len
);
407 DLIST_REMOVE(transport
->pending_recv
, req
);
408 req
->state
= SMB2_REQUEST_ERROR
;
415 return NT_STATUS_UNSUCCESSFUL
;
419 handle timeouts of individual smb requests
421 static void smb2_timeout_handler(struct tevent_context
*ev
, struct tevent_timer
*te
,
422 struct timeval t
, void *private_data
)
424 struct smb2_request
*req
= talloc_get_type(private_data
, struct smb2_request
);
426 if (req
->state
== SMB2_REQUEST_RECV
) {
427 DLIST_REMOVE(req
->transport
->pending_recv
, req
);
429 req
->status
= NT_STATUS_IO_TIMEOUT
;
430 req
->state
= SMB2_REQUEST_ERROR
;
440 static int smb2_request_destructor(struct smb2_request
*req
)
442 if (req
->state
== SMB2_REQUEST_RECV
) {
443 DLIST_REMOVE(req
->transport
->pending_recv
, req
);
448 static NTSTATUS
smb2_transport_raw_send(struct smb2_transport
*transport
,
449 struct smb2_request_buffer
*buffer
)
454 /* check if the transport is dead */
455 if (transport
->socket
->sock
== NULL
) {
456 return NT_STATUS_NET_WRITE_FAULT
;
459 _smb_setlen_tcp(buffer
->buffer
, buffer
->size
- NBT_HDR_SIZE
);
460 blob
= data_blob_const(buffer
->buffer
, buffer
->size
);
461 status
= packet_send(transport
->packet
, blob
);
462 if (!NT_STATUS_IS_OK(status
)) {
470 put a request into the send queue
472 void smb2_transport_send(struct smb2_request
*req
)
476 DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req
->seqnum
));
477 dump_data(5, req
->out
.body
, req
->out
.body_size
);
479 if (req
->transport
->compound
.missing
> 0) {
484 end
= req
->out
.buffer
+ req
->out
.size
;
487 * we need to set dynamic otherwise
488 * smb2_grow_buffer segfaults
490 if (req
->out
.dynamic
== NULL
) {
491 req
->out
.dynamic
= end
;
494 next_ofs
= end
- req
->out
.hdr
;
495 if ((next_ofs
% 8) > 0) {
496 pad
= 8 - (next_ofs
% 8);
500 status
= smb2_grow_buffer(&req
->out
, pad
);
501 if (!NT_STATUS_IS_OK(status
)) {
502 req
->state
= SMB2_REQUEST_ERROR
;
503 req
->status
= status
;
506 req
->out
.size
+= pad
;
508 SIVAL(req
->out
.hdr
, SMB2_HDR_NEXT_COMMAND
, next_ofs
);
511 /* possibly sign the message */
512 if (req
->session
&& req
->session
->signing_active
) {
513 status
= smb2_sign_message(&req
->out
, req
->session
->session_key
);
514 if (!NT_STATUS_IS_OK(status
)) {
515 req
->state
= SMB2_REQUEST_ERROR
;
516 req
->status
= status
;
521 if (req
->transport
->compound
.missing
> 0) {
522 req
->transport
->compound
.buffer
= req
->out
;
524 status
= smb2_transport_raw_send(req
->transport
,
526 if (!NT_STATUS_IS_OK(status
)) {
527 req
->state
= SMB2_REQUEST_ERROR
;
528 req
->status
= status
;
532 ZERO_STRUCT(req
->out
);
534 req
->state
= SMB2_REQUEST_RECV
;
535 DLIST_ADD(req
->transport
->pending_recv
, req
);
538 if (req
->transport
->options
.request_timeout
) {
539 tevent_add_timer(req
->transport
->socket
->event
.ctx
, req
,
540 timeval_current_ofs(req
->transport
->options
.request_timeout
, 0),
541 smb2_timeout_handler
, req
);
544 talloc_set_destructor(req
, smb2_request_destructor
);
547 NTSTATUS
smb2_transport_compound_start(struct smb2_transport
*transport
,
550 ZERO_STRUCT(transport
->compound
);
551 transport
->compound
.missing
= num
;
555 void smb2_transport_compound_set_related(struct smb2_transport
*transport
,
558 transport
->compound
.related
= related
;
561 void smb2_transport_credits_ask_num(struct smb2_transport
*transport
,
564 transport
->credits
.ask_num
= ask_num
;
567 void smb2_transport_credits_set_charge(struct smb2_transport
*transport
,
570 transport
->credits
.charge
= charge
;
573 static void idle_handler(struct tevent_context
*ev
,
574 struct tevent_timer
*te
, struct timeval t
, void *private_data
)
576 struct smb2_transport
*transport
= talloc_get_type(private_data
,
577 struct smb2_transport
);
578 struct timeval next
= timeval_add(&t
, 0, transport
->idle
.period
);
579 transport
->socket
->event
.te
= tevent_add_timer(transport
->socket
->event
.ctx
,
582 idle_handler
, transport
);
583 transport
->idle
.func(transport
, transport
->idle
.private_data
);
587 setup the idle handler for a transport
588 the period is in microseconds
590 void smb2_transport_idle_handler(struct smb2_transport
*transport
,
591 void (*idle_func
)(struct smb2_transport
*, void *),
595 transport
->idle
.func
= idle_func
;
596 transport
->idle
.private_data
= private_data
;
597 transport
->idle
.period
= period
;
599 if (transport
->socket
->event
.te
!= NULL
) {
600 talloc_free(transport
->socket
->event
.te
);
603 transport
->socket
->event
.te
= tevent_add_timer(transport
->socket
->event
.ctx
,
605 timeval_current_ofs(0, period
),
606 idle_handler
, transport
);