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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "libcli/raw/libcliraw.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 "include/dlinklist.h"
34 an event has happened on the socket
36 static void smb2_transport_event_handler(struct event_context *ev,
38 uint16_t flags, void *private)
40 struct smb2_transport *transport = talloc_get_type(private,
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(void *ptr)
56 struct smb2_transport *transport = ptr;
57 smb2_transport_dead(transport);
65 static void smb2_transport_error(void *private, NTSTATUS status)
67 struct smb2_transport *transport = talloc_get_type(private,
68 struct smb2_transport);
69 smb2_transport_dead(transport);
72 static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob);
75 create a transport structure based on an established socket
77 struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
78 TALLOC_CTX *parent_ctx)
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);
87 /* setup the stream -> packet parser */
88 transport->packet = packet_init(transport);
89 if (transport->packet == NULL) {
90 talloc_free(transport);
93 packet_set_private(transport->packet, transport);
94 packet_set_socket(transport->packet, transport->socket->sock);
95 packet_set_callback(transport->packet, smb2_transport_finish_recv);
96 packet_set_full_request(transport->packet, packet_full_request_nbt);
97 packet_set_error_handler(transport->packet, smb2_transport_error);
98 packet_set_event_context(transport->packet, transport->socket->event.ctx);
99 packet_set_nofree(transport->packet);
101 /* take over event handling from the socket layer - it only
102 handles events up until we are connected */
103 talloc_free(transport->socket->event.fde);
104 transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
106 socket_get_fd(transport->socket->sock),
108 smb2_transport_event_handler,
111 packet_set_fde(transport->packet, transport->socket->event.fde);
112 packet_set_serialise(transport->packet);
114 talloc_set_destructor(transport, transport_destructor);
116 transport->options.timeout = 30;
122 mark the transport as dead
124 void smb2_transport_dead(struct smb2_transport *transport)
126 smbcli_sock_dead(transport->socket);
128 /* kill all pending receives */
129 while (transport->pending_recv) {
130 struct smb2_request *req = transport->pending_recv;
131 req->state = SMB2_REQUEST_ERROR;
132 req->status = NT_STATUS_NET_WRITE_FAULT;
133 DLIST_REMOVE(transport->pending_recv, req);
141 we have a full request in our receive buffer - match it to a pending request
144 static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
146 struct smb2_transport *transport = talloc_get_type(private,
147 struct smb2_transport);
148 uint8_t *buffer, *hdr;
150 struct smb2_request *req = NULL;
152 uint16_t buffer_code;
153 uint32_t dynamic_size;
158 hdr = buffer+NBT_HDR_SIZE;
160 if (len < SMB2_MIN_SIZE) {
161 DEBUG(1,("Discarding smb2 reply of size %d\n", len));
165 seqnum = BVAL(hdr, SMB2_HDR_SEQNUM);
167 /* match the incoming request against the list of pending requests */
168 for (req=transport->pending_recv; req; req=req->next) {
169 if (req->seqnum == seqnum) break;
173 DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
174 (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
178 /* fill in the 'in' portion of the matching request */
179 req->in.buffer = buffer;
180 talloc_steal(req, buffer);
182 req->in.allocated = req->in.size;
185 req->in.body = hdr+SMB2_HDR_BODY;
186 req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
187 req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
189 if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
190 /* the server has helpfully told us that this request is still being
191 processed. how useful :) */
196 buffer_code = SVAL(req->in.body, 0);
197 req->in.dynamic = NULL;
198 dynamic_size = req->in.body_size - (buffer_code & ~1);
199 if (dynamic_size != 0 && (buffer_code & 1)) {
200 req->in.dynamic = req->in.body + (buffer_code & ~1);
201 if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
202 DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
208 DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
209 dump_data(5, req->in.body, req->in.body_size);
211 /* if this request has an async handler then call that to
212 notify that the reply has been received. This might destroy
213 the request so it must happen last */
214 DLIST_REMOVE(transport->pending_recv, req);
215 req->state = SMB2_REQUEST_DONE;
222 dump_data(5, buffer, len);
224 DLIST_REMOVE(transport->pending_recv, req);
225 req->state = SMB2_REQUEST_ERROR;
232 return NT_STATUS_UNSUCCESSFUL;
236 handle timeouts of individual smb requests
238 static void smb2_timeout_handler(struct event_context *ev, struct timed_event *te,
239 struct timeval t, void *private)
241 struct smb2_request *req = talloc_get_type(private, struct smb2_request);
243 if (req->state == SMB2_REQUEST_RECV) {
244 DLIST_REMOVE(req->transport->pending_recv, req);
246 req->status = NT_STATUS_IO_TIMEOUT;
247 req->state = SMB2_REQUEST_ERROR;
257 static int smb2_request_destructor(void *ptr)
259 struct smb2_request *req = talloc_get_type(ptr, struct smb2_request);
260 if (req->state == SMB2_REQUEST_RECV) {
261 DLIST_REMOVE(req->transport->pending_recv, req);
268 put a request into the send queue
270 void smb2_transport_send(struct smb2_request *req)
275 _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
277 DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
278 dump_data(5, req->out.body, req->out.body_size);
280 /* check if the transport is dead */
281 if (req->transport->socket->sock == NULL) {
282 req->state = SMB2_REQUEST_ERROR;
283 req->status = NT_STATUS_NET_WRITE_FAULT;
287 blob = data_blob_const(req->out.buffer, req->out.size);
288 status = packet_send(req->transport->packet, blob);
289 if (!NT_STATUS_IS_OK(status)) {
290 req->state = SMB2_REQUEST_ERROR;
291 req->status = status;
295 req->state = SMB2_REQUEST_RECV;
296 DLIST_ADD(req->transport->pending_recv, req);
299 if (req->transport->options.timeout) {
300 event_add_timed(req->transport->socket->event.ctx, req,
301 timeval_current_ofs(req->transport->options.timeout, 0),
302 smb2_timeout_handler, req);
305 talloc_set_destructor(req, smb2_request_destructor);