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
& EVENT_FD_READ
) {
43 packet_recv(transport
->packet
);
46 if (flags
& EVENT_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
= event_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 /* match the incoming request against the list of pending requests */
281 for (req
=transport
->pending_recv
; req
; req
=req
->next
) {
282 if (req
->seqnum
== seqnum
) break;
286 DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
287 (long long)seqnum
, SVAL(hdr
, SMB2_HDR_OPCODE
)));
291 /* fill in the 'in' portion of the matching request */
292 req
->in
.buffer
= buffer
;
293 talloc_steal(req
, buffer
);
295 req
->in
.allocated
= req
->in
.size
;
298 req
->in
.body
= hdr
+SMB2_HDR_BODY
;
299 req
->in
.body_size
= req
->in
.size
- (SMB2_HDR_BODY
+NBT_HDR_SIZE
);
300 req
->status
= NT_STATUS(IVAL(hdr
, SMB2_HDR_STATUS
));
302 if ((flags
& SMB2_HDR_FLAG_ASYNC
) &&
303 NT_STATUS_EQUAL(req
->status
, STATUS_PENDING
)) {
304 req
->cancel
.can_cancel
= true;
305 req
->cancel
.async_id
= BVAL(hdr
, SMB2_HDR_ASYNC_ID
);
306 for (i
=0; i
< req
->cancel
.do_cancel
; i
++) {
313 next_ofs
= IVAL(req
->in
.hdr
, SMB2_HDR_NEXT_COMMAND
);
315 if (smb2_oob(&req
->in
, req
->in
.hdr
+ next_ofs
, SMB2_HDR_BODY
+ 2)) {
316 DEBUG(1,("SMB2 request invalid next offset 0x%x\n",
321 req
->in
.size
= NBT_HDR_SIZE
+ next_ofs
;
322 req
->in
.body_size
= req
->in
.size
- (SMB2_HDR_BODY
+NBT_HDR_SIZE
);
325 if (req
->session
&& req
->session
->signing_active
) {
326 status
= smb2_check_signature(&req
->in
,
327 req
->session
->session_key
);
328 if (!NT_STATUS_IS_OK(status
)) {
329 /* the spec says to ignore packets with a bad signature */
335 buffer_code
= SVAL(req
->in
.body
, 0);
336 req
->in
.body_fixed
= (buffer_code
& ~1);
337 req
->in
.dynamic
= NULL
;
338 dynamic_size
= req
->in
.body_size
- req
->in
.body_fixed
;
339 if (dynamic_size
!= 0 && (buffer_code
& 1)) {
340 req
->in
.dynamic
= req
->in
.body
+ req
->in
.body_fixed
;
341 if (smb2_oob(&req
->in
, req
->in
.dynamic
, dynamic_size
)) {
342 DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
348 smb2_setup_bufinfo(req
);
350 DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req
->seqnum
));
351 dump_data(5, req
->in
.body
, req
->in
.body_size
);
354 struct tevent_immediate
*im
;
355 struct smb2_transport_compount_response_state
*state
;
357 state
= talloc(transport
,
358 struct smb2_transport_compount_response_state
);
362 state
->transport
= transport
;
364 state
->blob
= data_blob_talloc(state
, NULL
,
365 blob
.length
- next_ofs
);
366 if (!state
->blob
.data
) {
369 im
= tevent_create_immediate(state
);
374 _smb2_setlen(state
->blob
.data
, state
->blob
.length
- NBT_HDR_SIZE
);
375 memcpy(state
->blob
.data
+ NBT_HDR_SIZE
,
376 req
->in
.hdr
+ next_ofs
,
377 req
->in
.allocated
- req
->in
.size
);
378 tevent_schedule_immediate(im
, transport
->socket
->event
.ctx
,
379 smb2_transport_compound_response_handler
,
383 /* if this request has an async handler then call that to
384 notify that the reply has been received. This might destroy
385 the request so it must happen last */
386 DLIST_REMOVE(transport
->pending_recv
, req
);
387 req
->state
= SMB2_REQUEST_DONE
;
394 dump_data(5, buffer
, len
);
396 DLIST_REMOVE(transport
->pending_recv
, req
);
397 req
->state
= SMB2_REQUEST_ERROR
;
404 return NT_STATUS_UNSUCCESSFUL
;
408 handle timeouts of individual smb requests
410 static void smb2_timeout_handler(struct tevent_context
*ev
, struct tevent_timer
*te
,
411 struct timeval t
, void *private_data
)
413 struct smb2_request
*req
= talloc_get_type(private_data
, struct smb2_request
);
415 if (req
->state
== SMB2_REQUEST_RECV
) {
416 DLIST_REMOVE(req
->transport
->pending_recv
, req
);
418 req
->status
= NT_STATUS_IO_TIMEOUT
;
419 req
->state
= SMB2_REQUEST_ERROR
;
429 static int smb2_request_destructor(struct smb2_request
*req
)
431 if (req
->state
== SMB2_REQUEST_RECV
) {
432 DLIST_REMOVE(req
->transport
->pending_recv
, req
);
437 static NTSTATUS
smb2_transport_raw_send(struct smb2_transport
*transport
,
438 struct smb2_request_buffer
*buffer
)
443 /* check if the transport is dead */
444 if (transport
->socket
->sock
== NULL
) {
445 return NT_STATUS_NET_WRITE_FAULT
;
448 _smb2_setlen(buffer
->buffer
, buffer
->size
- NBT_HDR_SIZE
);
449 blob
= data_blob_const(buffer
->buffer
, buffer
->size
);
450 status
= packet_send(transport
->packet
, blob
);
451 if (!NT_STATUS_IS_OK(status
)) {
459 put a request into the send queue
461 void smb2_transport_send(struct smb2_request
*req
)
465 DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req
->seqnum
));
466 dump_data(5, req
->out
.body
, req
->out
.body_size
);
468 if (req
->transport
->compound
.missing
> 0) {
473 end
= req
->out
.buffer
+ req
->out
.size
;
476 * we need to set dynamic otherwise
477 * smb2_grow_buffer segfaults
479 if (req
->out
.dynamic
== NULL
) {
480 req
->out
.dynamic
= end
;
483 next_ofs
= end
- req
->out
.hdr
;
484 if ((next_ofs
% 8) > 0) {
485 pad
= 8 - (next_ofs
% 8);
489 status
= smb2_grow_buffer(&req
->out
, pad
);
490 if (!NT_STATUS_IS_OK(status
)) {
491 req
->state
= SMB2_REQUEST_ERROR
;
492 req
->status
= status
;
495 req
->out
.size
+= pad
;
497 SIVAL(req
->out
.hdr
, SMB2_HDR_NEXT_COMMAND
, next_ofs
);
500 /* possibly sign the message */
501 if (req
->session
&& req
->session
->signing_active
) {
502 status
= smb2_sign_message(&req
->out
, req
->session
->session_key
);
503 if (!NT_STATUS_IS_OK(status
)) {
504 req
->state
= SMB2_REQUEST_ERROR
;
505 req
->status
= status
;
510 if (req
->transport
->compound
.missing
> 0) {
511 req
->transport
->compound
.buffer
= req
->out
;
513 status
= smb2_transport_raw_send(req
->transport
,
515 if (!NT_STATUS_IS_OK(status
)) {
516 req
->state
= SMB2_REQUEST_ERROR
;
517 req
->status
= status
;
521 ZERO_STRUCT(req
->out
);
523 req
->state
= SMB2_REQUEST_RECV
;
524 DLIST_ADD(req
->transport
->pending_recv
, req
);
527 if (req
->transport
->options
.request_timeout
) {
528 event_add_timed(req
->transport
->socket
->event
.ctx
, req
,
529 timeval_current_ofs(req
->transport
->options
.request_timeout
, 0),
530 smb2_timeout_handler
, req
);
533 talloc_set_destructor(req
, smb2_request_destructor
);
536 NTSTATUS
smb2_transport_compound_start(struct smb2_transport
*transport
,
539 ZERO_STRUCT(transport
->compound
);
540 transport
->compound
.missing
= num
;
544 void smb2_transport_compound_set_related(struct smb2_transport
*transport
,
547 transport
->compound
.related
= related
;
550 void smb2_transport_credits_ask_num(struct smb2_transport
*transport
,
553 transport
->credits
.ask_num
= ask_num
;
556 void smb2_transport_credits_set_charge(struct smb2_transport
*transport
,
559 transport
->credits
.charge
= charge
;
562 static void idle_handler(struct tevent_context
*ev
,
563 struct tevent_timer
*te
, struct timeval t
, void *private_data
)
565 struct smb2_transport
*transport
= talloc_get_type(private_data
,
566 struct smb2_transport
);
567 struct timeval next
= timeval_add(&t
, 0, transport
->idle
.period
);
568 transport
->socket
->event
.te
= event_add_timed(transport
->socket
->event
.ctx
,
571 idle_handler
, transport
);
572 transport
->idle
.func(transport
, transport
->idle
.private_data
);
576 setup the idle handler for a transport
577 the period is in microseconds
579 void smb2_transport_idle_handler(struct smb2_transport
*transport
,
580 void (*idle_func
)(struct smb2_transport
*, void *),
584 transport
->idle
.func
= idle_func
;
585 transport
->idle
.private_data
= private_data
;
586 transport
->idle
.period
= period
;
588 if (transport
->socket
->event
.te
!= NULL
) {
589 talloc_free(transport
->socket
->event
.te
);
592 transport
->socket
->event
.te
= event_add_timed(transport
->socket
->event
.ctx
,
594 timeval_current_ofs(0, period
),
595 idle_handler
, transport
);