2 Unix SMB/CIFS implementation.
3 SMB client transport context management functions
5 Copyright (C) Andrew Tridgell 1994-2005
6 Copyright (C) James Myers 2003 <myersjj@samba.org>
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 "../lib/async_req/async_sock.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "lib/socket/socket.h"
29 #include "lib/events/events.h"
30 #include "librpc/gen_ndr/ndr_nbt.h"
31 #include "../libcli/nbt/libnbt.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "../libcli/smb/read_smb.h"
38 static int transport_destructor(struct smbcli_transport
*transport
)
40 smbcli_transport_dead(transport
, NT_STATUS_LOCAL_DISCONNECT
);
45 create a transport structure based on an established socket
47 struct smbcli_transport
*smbcli_transport_init(struct smbcli_socket
*sock
,
48 TALLOC_CTX
*parent_ctx
,
50 struct smbcli_options
*options
)
52 struct smbcli_transport
*transport
;
53 uint32_t smb1_capabilities
;
55 transport
= talloc_zero(parent_ctx
, struct smbcli_transport
);
56 if (!transport
) return NULL
;
58 transport
->ev
= sock
->event
.ctx
;
59 transport
->options
= *options
;
61 if (transport
->options
.max_protocol
== PROTOCOL_DEFAULT
) {
62 transport
->options
.max_protocol
= PROTOCOL_NT1
;
65 if (transport
->options
.max_protocol
> PROTOCOL_NT1
) {
66 transport
->options
.max_protocol
= PROTOCOL_NT1
;
69 TALLOC_FREE(sock
->event
.fde
);
70 TALLOC_FREE(sock
->event
.te
);
72 smb1_capabilities
= 0;
73 smb1_capabilities
|= CAP_LARGE_FILES
;
74 smb1_capabilities
|= CAP_NT_SMBS
| CAP_RPC_REMOTE_APIS
;
75 smb1_capabilities
|= CAP_LOCK_AND_READ
| CAP_NT_FIND
;
76 smb1_capabilities
|= CAP_DFS
| CAP_W2K_SMBS
;
77 smb1_capabilities
|= CAP_LARGE_READX
|CAP_LARGE_WRITEX
;
78 smb1_capabilities
|= CAP_LWIO
;
80 if (options
->ntstatus_support
) {
81 smb1_capabilities
|= CAP_STATUS32
;
84 if (options
->unicode
) {
85 smb1_capabilities
|= CAP_UNICODE
;
88 if (options
->use_spnego
) {
89 smb1_capabilities
|= CAP_EXTENDED_SECURITY
;
92 if (options
->use_level2_oplocks
) {
93 smb1_capabilities
|= CAP_LEVEL_II_OPLOCKS
;
96 transport
->conn
= smbXcli_conn_create(transport
,
101 NULL
, /* client_guid */
102 0, /* smb2_capabilities */
103 NULL
); /* smb3_ciphers */
104 if (transport
->conn
== NULL
) {
106 TALLOC_FREE(transport
);
112 talloc_set_destructor(transport
, transport_destructor
);
118 create a transport structure based on an established socket
120 NTSTATUS
smbcli_transport_raw_init(TALLOC_CTX
*mem_ctx
,
121 struct tevent_context
*ev
,
122 struct smbXcli_conn
**_conn
,
123 const struct smbcli_options
*options
,
124 struct smbcli_transport
**_transport
)
126 struct smbcli_transport
*transport
= NULL
;
129 if (*_conn
== NULL
) {
130 return NT_STATUS_INVALID_PARAMETER
;
133 transport
= talloc_zero(mem_ctx
, struct smbcli_transport
);
134 if (transport
== NULL
) {
135 return NT_STATUS_NO_MEMORY
;
139 transport
->options
= *options
;
142 * First only set the pointer without move.
144 transport
->conn
= *_conn
;
145 status
= smb_raw_negotiate_fill_transport(transport
);
146 if (!NT_STATUS_IS_OK(status
)) {
147 TALLOC_FREE(transport
);
151 talloc_set_destructor(transport
, transport_destructor
);
154 * Now move it away from the caller...
156 transport
->conn
= talloc_move(transport
, _conn
);
157 *_transport
= transport
;
162 mark the transport as dead
164 void smbcli_transport_dead(struct smbcli_transport
*transport
, NTSTATUS status
)
166 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL
, status
)) {
167 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
169 if (NT_STATUS_IS_OK(status
)) {
170 status
= NT_STATUS_LOCAL_DISCONNECT
;
173 smbXcli_conn_disconnect(transport
->conn
, status
);
176 static void idle_handler(struct tevent_context
*ev
,
177 struct tevent_timer
*te
, struct timeval t
, void *private_data
)
179 struct smbcli_transport
*transport
= talloc_get_type(private_data
,
180 struct smbcli_transport
);
183 transport
->idle
.func(transport
, transport
->idle
.private_data
);
185 if (transport
->idle
.func
== NULL
) {
189 if (!smbXcli_conn_is_connected(transport
->conn
)) {
193 next
= timeval_current_ofs_usec(transport
->idle
.period
);
195 transport
->idle
.te
= tevent_add_timer(transport
->ev
,
203 setup the idle handler for a transport
204 the period is in microseconds
206 _PUBLIC_
void smbcli_transport_idle_handler(struct smbcli_transport
*transport
,
207 void (*idle_func
)(struct smbcli_transport
*, void *),
211 TALLOC_FREE(transport
->idle
.te
);
212 ZERO_STRUCT(transport
->idle
);
214 if (idle_func
== NULL
) {
218 if (!smbXcli_conn_is_connected(transport
->conn
)) {
222 transport
->idle
.func
= idle_func
;
223 transport
->idle
.private_data
= private_data
;
224 transport
->idle
.period
= period
;
226 transport
->idle
.te
= tevent_add_timer(transport
->ev
,
228 timeval_current_ofs_usec(period
),
234 process some read/write requests that are pending
235 return false if the socket is dead
237 _PUBLIC_
bool smbcli_transport_process(struct smbcli_transport
*transport
)
239 struct tevent_req
*subreq
= NULL
;
242 if (!smbXcli_conn_is_connected(transport
->conn
)) {
246 if (!smbXcli_conn_has_async_calls(transport
->conn
)) {
251 * do not block for more than 500 micro seconds
253 subreq
= tevent_wakeup_send(transport
,
255 timeval_current_ofs_usec(500));
256 if (subreq
== NULL
) {
260 ret
= tevent_loop_once(transport
->ev
);
267 if (!smbXcli_conn_is_connected(transport
->conn
)) {
274 static void smbcli_transport_break_handler(struct tevent_req
*subreq
);
275 static void smbcli_request_done(struct tevent_req
*subreq
);
277 struct tevent_req
*smbcli_transport_setup_subreq(struct smbcli_request
*req
)
279 struct smbcli_transport
*transport
= req
->transport
;
281 uint8_t additional_flags
;
283 uint16_t additional_flags2
;
284 uint16_t clear_flags2
;
286 struct smbXcli_tcon
*tcon
= NULL
;
287 struct smbXcli_session
*session
= NULL
;
288 uint32_t timeout_msec
= transport
->options
.request_timeout
* 1000;
289 struct iovec
*bytes_iov
= NULL
;
290 struct tevent_req
*subreq
= NULL
;
292 smb_command
= SVAL(req
->out
.hdr
, HDR_COM
);
293 additional_flags
= CVAL(req
->out
.hdr
, HDR_FLG
);
294 additional_flags2
= SVAL(req
->out
.hdr
, HDR_FLG2
);
295 pid
= SVAL(req
->out
.hdr
, HDR_PID
);
296 pid
|= SVAL(req
->out
.hdr
, HDR_PIDHIGH
)<<16;
298 clear_flags
= ~additional_flags
;
299 clear_flags2
= ~additional_flags2
;
302 session
= req
->session
->smbXcli
;
306 tcon
= req
->tree
->smbXcli
;
309 bytes_iov
= talloc(req
, struct iovec
);
310 if (bytes_iov
== NULL
) {
313 bytes_iov
->iov_base
= (void *)req
->out
.data
;
314 bytes_iov
->iov_len
= req
->out
.data_size
;
316 subreq
= smb1cli_req_create(req
,
329 (uint16_t *)req
->out
.vwv
,
331 if (subreq
== NULL
) {
335 ZERO_STRUCT(req
->out
);
341 put a request into the send queue
343 void smbcli_transport_send(struct smbcli_request
*req
)
345 struct smbcli_transport
*transport
= req
->transport
;
347 bool need_pending_break
= false;
348 struct tevent_req
*subreq
= NULL
;
350 size_t num_subreqs
= 0;
352 if (transport
->oplock
.handler
) {
353 need_pending_break
= true;
356 if (transport
->break_subreq
) {
357 need_pending_break
= false;
360 if (need_pending_break
) {
361 subreq
= smb1cli_req_create(transport
,
365 0, /* additional_flags */
367 0, /* additional_flags2 */
368 0, /* clear_flags2 */
369 0, /* timeout_msec */
376 NULL
); /* bytes_iov */
377 if (subreq
!= NULL
) {
378 smb1cli_req_set_mid(subreq
, 0xFFFF);
379 smbXcli_req_set_pending(subreq
);
380 tevent_req_set_callback(subreq
,
381 smbcli_transport_break_handler
,
383 transport
->break_subreq
= subreq
;
388 subreq
= smbcli_transport_setup_subreq(req
);
389 if (subreq
== NULL
) {
390 req
->state
= SMBCLI_REQUEST_ERROR
;
391 req
->status
= NT_STATUS_NO_MEMORY
;
395 for (i
= 0; i
< ARRAY_SIZE(req
->subreqs
); i
++) {
396 if (req
->subreqs
[i
] == NULL
) {
397 req
->subreqs
[i
] = subreq
;
400 if (req
->subreqs
[i
] == NULL
) {
404 if (!tevent_req_is_in_progress(req
->subreqs
[i
])) {
405 req
->state
= SMBCLI_REQUEST_ERROR
;
406 req
->status
= NT_STATUS_INTERNAL_ERROR
;
412 req
->state
= SMBCLI_REQUEST_RECV
;
413 tevent_req_set_callback(req
->subreqs
[0], smbcli_request_done
, req
);
415 status
= smb1cli_req_chain_submit(req
->subreqs
, num_subreqs
);
416 if (!NT_STATUS_IS_OK(status
)) {
417 req
->status
= status
;
418 req
->state
= SMBCLI_REQUEST_ERROR
;
419 smbXcli_conn_disconnect(transport
->conn
, status
);
423 static void smbcli_request_done(struct tevent_req
*subreq
)
425 struct smbcli_request
*req
=
426 tevent_req_callback_data(subreq
,
427 struct smbcli_request
);
428 struct smbcli_transport
*transport
= req
->transport
;
433 uint16_t *vwv
= NULL
;
434 uint32_t num_bytes
= 0;
435 uint8_t *bytes
= NULL
;
436 struct iovec
*recv_iov
= NULL
;
437 uint8_t *inbuf
= NULL
;
439 req
->status
= smb1cli_req_recv(req
->subreqs
[0], req
,
444 NULL
, /* pvwv_offset */
447 NULL
, /* pbytes_offset */
449 NULL
, 0); /* expected */
450 TALLOC_FREE(req
->subreqs
[0]);
451 if (!NT_STATUS_IS_OK(req
->status
)) {
452 if (recv_iov
== NULL
) {
453 req
->state
= SMBCLI_REQUEST_ERROR
;
454 transport
->error
.e
.nt_status
= req
->status
;
455 transport
->error
.etype
= ETYPE_SOCKET
;
464 * For SMBreadBraw hdr is NULL
466 len
= recv_iov
[0].iov_len
;
467 for (i
=1; hdr
!= NULL
&& i
< 3; i
++) {
468 uint8_t *p
= recv_iov
[i
-1].iov_base
;
469 uint8_t *c1
= recv_iov
[i
].iov_base
;
470 uint8_t *c2
= p
+ recv_iov
[i
-1].iov_len
;
472 len
+= recv_iov
[i
].iov_len
;
477 if (recv_iov
[i
].iov_len
== 0) {
482 req
->state
= SMBCLI_REQUEST_ERROR
;
483 req
->status
= NT_STATUS_INTERNAL_ERROR
;
484 transport
->error
.e
.nt_status
= req
->status
;
485 transport
->error
.etype
= ETYPE_SMB
;
493 /* fill in the 'in' portion of the matching request */
494 req
->in
.buffer
= inbuf
;
495 req
->in
.size
= NBT_HDR_SIZE
+ len
;
496 req
->in
.allocated
= req
->in
.size
;
499 req
->in
.vwv
= (uint8_t *)vwv
;
501 req
->in
.data
= bytes
;
502 req
->in
.data_size
= num_bytes
;
503 req
->in
.ptr
= req
->in
.data
;
505 req
->flags2
= SVAL(req
->in
.hdr
, HDR_FLG2
);
508 smb_setup_bufinfo(req
);
510 transport
->error
.e
.nt_status
= req
->status
;
511 if (NT_STATUS_IS_OK(req
->status
)) {
512 transport
->error
.etype
= ETYPE_NONE
;
514 transport
->error
.etype
= ETYPE_SMB
;
517 req
->state
= SMBCLI_REQUEST_DONE
;
523 static void smbcli_transport_break_handler(struct tevent_req
*subreq
)
525 struct smbcli_transport
*transport
=
526 tevent_req_callback_data(subreq
,
527 struct smbcli_transport
);
529 struct iovec
*recv_iov
= NULL
;
531 uint16_t *vwv
= NULL
;
532 const struct smb1cli_req_expected_response expected
[] = {
534 .status
= NT_STATUS_OK
,
542 transport
->break_subreq
= NULL
;
544 status
= smb1cli_req_recv(subreq
, transport
,
549 NULL
, /* pvwv_offset */
550 NULL
, /* pnum_bytes */
552 NULL
, /* pbytes_offset */
555 ARRAY_SIZE(expected
));
557 if (!NT_STATUS_IS_OK(status
)) {
558 TALLOC_FREE(recv_iov
);
559 smbcli_transport_dead(transport
, status
);
564 * Setup the subreq to handle the
565 * next incoming SMB2 Break.
567 subreq
= smb1cli_req_create(transport
,
571 0, /* additional_flags */
573 0, /* additional_flags2 */
574 0, /* clear_flags2 */
575 0, /* timeout_msec */
582 NULL
); /* bytes_iov */
583 if (subreq
!= NULL
) {
584 smb1cli_req_set_mid(subreq
, 0xFFFF);
585 smbXcli_req_set_pending(subreq
);
586 tevent_req_set_callback(subreq
,
587 smbcli_transport_break_handler
,
589 transport
->break_subreq
= subreq
;
592 tid
= SVAL(hdr
, HDR_TID
);
593 fnum
= SVAL(vwv
+2, 0);
594 level
= CVAL(vwv
+3, 1);
596 TALLOC_FREE(recv_iov
);
598 if (transport
->oplock
.handler
) {
599 transport
->oplock
.handler(transport
, tid
, fnum
, level
,
600 transport
->oplock
.private_data
);
602 DEBUG(5,("Got SMB oplock break with no handler\n"));
608 /****************************************************************************
609 Send an SMBecho (async send)
610 *****************************************************************************/
611 _PUBLIC_
struct smbcli_request
*smb_raw_echo_send(struct smbcli_transport
*transport
,
614 struct smbcli_request
*req
;
616 req
= smbcli_request_setup_transport(transport
, SMBecho
, 1, p
->in
.size
);
617 if (!req
) return NULL
;
619 SSVAL(req
->out
.vwv
, VWV(0), p
->in
.repeat_count
);
621 memcpy(req
->out
.data
, p
->in
.data
, p
->in
.size
);
625 if (!smbcli_request_send(req
)) {
626 smbcli_request_destroy(req
);
633 /****************************************************************************
634 raw echo interface (async recv)
635 ****************************************************************************/
636 NTSTATUS
smb_raw_echo_recv(struct smbcli_request
*req
, TALLOC_CTX
*mem_ctx
,
639 if (!smbcli_request_receive(req
) ||
640 smbcli_request_is_error(req
)) {
644 SMBCLI_CHECK_WCT(req
, 1);
646 p
->out
.sequence_number
= SVAL(req
->in
.vwv
, VWV(0));
647 p
->out
.size
= req
->in
.data_size
;
648 talloc_free(p
->out
.data
);
649 p
->out
.data
= talloc_array(mem_ctx
, uint8_t, p
->out
.size
);
650 NT_STATUS_HAVE_NO_MEMORY(p
->out
.data
);
652 if (!smbcli_raw_pull_data(&req
->in
.bufinfo
, req
->in
.data
, p
->out
.size
, p
->out
.data
)) {
653 req
->status
= NT_STATUS_BUFFER_TOO_SMALL
;
656 if (p
->out
.count
== p
->in
.repeat_count
) {
657 return smbcli_request_destroy(req
);
663 return smbcli_request_destroy(req
);
666 /****************************************************************************
667 Send a echo (sync interface)
668 *****************************************************************************/
669 NTSTATUS
smb_raw_echo(struct smbcli_transport
*transport
, struct smb_echo
*p
)
671 struct smbcli_request
*req
= smb_raw_echo_send(transport
, p
);
672 return smbcli_request_simple_recv(req
);