summaryrefslogtreecommitdiff
path: root/source4/libcli
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli')
-rw-r--r--source4/libcli/config.mk2
-rw-r--r--source4/libcli/smb2/config.mk6
-rw-r--r--source4/libcli/smb2/negprot.c82
-rw-r--r--source4/libcli/smb2/request.c171
-rw-r--r--source4/libcli/smb2/smb2.h154
-rw-r--r--source4/libcli/smb2/transport.c274
6 files changed, 689 insertions, 0 deletions
diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk
index ea93b12a03..8417d770b6 100644
--- a/source4/libcli/config.mk
+++ b/source4/libcli/config.mk
@@ -122,3 +122,5 @@ OBJ_FILES = raw/rawfile.o \
raw/rawdate.o \
raw/rawlpq.o
REQUIRED_SUBSYSTEMS = LIBPACKET
+
+include smb2/config.mk
diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk
new file mode 100644
index 0000000000..9840876c2f
--- /dev/null
+++ b/source4/libcli/smb2/config.mk
@@ -0,0 +1,6 @@
+[SUBSYSTEM::LIBCLI_SMB2]
+OBJ_FILES = \
+ transport.o \
+ request.o \
+ negprot.o
+REQUIRED_SUBSYSTEMS = LIBCLI_RAW LIBPACKET
diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c
new file mode 100644
index 0000000000..6b35373807
--- /dev/null
+++ b/source4/libcli/smb2/negprot.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client negprot handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+
+/*
+ send a negprot request
+*/
+struct smb2_request *smb2_negprot_send(struct smb2_transport *transport)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(transport, SMB2_OP_NEGPROT, 0x26);
+ if (req == NULL) return NULL;
+
+ memset(req->out.body, 0, 0x26);
+ SIVAL(req->out.body, 0, 0x00010024); /* unknown */
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ recv a negprot reply
+*/
+NTSTATUS smb2_negprot_recv(struct smb2_request *req)
+{
+ NTTIME t1, t2;
+ DATA_BLOB secblob;
+ struct GUID guid;
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ t1 = smbcli_pull_nttime(req->in.body, 0x28);
+ t2 = smbcli_pull_nttime(req->in.body, 0x30);
+
+ secblob = smb2_pull_blob(req, req->in.body+0x40, req->in.body_size - 0x40);
+ status = smb2_pull_guid(req, req->in.body+0x08, &guid);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ printf("Negprot reply:\n");
+ printf("t1 =%s\n", nt_time_string(req, t1));
+ printf("t2 =%s\n", nt_time_string(req, t2));
+ printf("guid=%s\n", GUID_string(req, &guid));
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync negprot request
+*/
+NTSTATUS smb2_negprot(struct smb2_transport *transport)
+{
+ struct smb2_request *req = smb2_negprot_send(transport);
+ return smb2_negprot_recv(req);
+}
diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c
new file mode 100644
index 0000000000..1b2dc5e64c
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client request handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "include/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/*
+ initialise a smb2 request
+*/
+struct smb2_request *smb2_request_init(struct smb2_transport *transport,
+ uint16_t opcode, uint32_t body_size)
+{
+ struct smb2_request *req;
+
+ req = talloc(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+ req->seqnum = transport->seqnum++;
+ req->status = NT_STATUS_OK;
+ req->async.fn = NULL;
+ req->next = req->prev = NULL;
+
+ ZERO_STRUCT(req->in);
+
+ req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_size;
+ req->out.buffer = talloc_size(req, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE + body_size;
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_size = body_size;
+ req->out.ptr = req->out.body;
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
+ SSVAL(req->out.hdr, SMB2_HDR_PAD2, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_UID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_UID2, 0);
+ memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
+
+ return req;
+}
+
+/* destroy a request structure and return final status */
+NTSTATUS smb2_request_destroy(struct smb2_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+
+ if (req->state == SMBCLI_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = req->status;
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a response to a packet
+*/
+BOOL smb2_request_receive(struct smb2_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return False;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return False;
+ }
+ }
+
+ return req->state == SMB2_REQUEST_DONE;
+}
+
+/* Return true if the last packet was in error */
+BOOL smb2_request_is_error(struct smb2_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/*
+ check if a range in the reply body is out of bounds
+*/
+BOOL smb2_oob(struct smb2_request *req, const uint8_t *ptr, uint_t size)
+{
+ /* be careful with wraparound! */
+ if (ptr < req->in.body ||
+ ptr >= req->in.body + req->in.body_size ||
+ size > req->in.body_size ||
+ ptr + size > req->in.body + req->in.body_size) {
+ return True;
+ }
+ return False;
+}
+
+/*
+ pull a data blob from the body of a reply
+*/
+DATA_BLOB smb2_pull_blob(struct smb2_request *req, uint8_t *ptr, uint_t size)
+{
+ if (smb2_oob(req, ptr, size)) {
+ return data_blob(NULL, 0);
+ }
+ return data_blob_talloc(req, ptr, size);
+}
+
+/*
+ pull a guid from the reply body
+*/
+NTSTATUS smb2_pull_guid(struct smb2_request *req, uint8_t *ptr, struct GUID *guid)
+{
+ NTSTATUS status;
+ DATA_BLOB blob = smb2_pull_blob(req, ptr, 16);
+ if (blob.data == NULL) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ status = ndr_pull_struct_blob(&blob, req, guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ data_blob_free(&blob);
+ return status;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000000..2e01159355
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,154 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client library header
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+struct smb2_options {
+ uint32_t timeout;
+};
+
+/*
+ information returned from the negotiate response
+*/
+struct smb2_negotiate {
+ DATA_BLOB secblob;
+
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ struct smb2_options options;
+ struct smb2_negotiate negotiate;
+
+ /* next seqnum to allocate */
+ uint64_t seqnum;
+
+ /* a list of requests that are pending for receive on this
+ connection */
+ struct smb2_request *pending_recv;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+};
+
+
+struct smb2_request_buffer {
+ /* the raw SMB2 buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ uint_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ uint_t allocated;
+
+ /* the start of the SMB2 header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the packet body */
+ uint8_t *body;
+ uint_t body_size;
+
+ /* ptr is used as a moving pointer into the data area
+ * of the packet. The reason its here and not a local
+ * variable in each function is that when a realloc of
+ * a send packet is done we need to move this
+ * pointer */
+ uint8_t *ptr;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */
+ SMB2_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMB2_REQUEST_DONE, /* the request is finished */
+ SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB2 request */
+struct smb2_request {
+ /* allow a request to be part of a list of requests */
+ struct smb2_request *next, *prev;
+
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct smb2_transport *transport;
+
+ uint64_t seqnum;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smb2_request *);
+ void *private;
+ } async;
+};
+
+
+#define SMB2_MIN_SIZE 0x40
+
+/* offsets into header elements */
+#define SMB2_HDR_LENGTH 0x04
+#define SMB2_HDR_PAD1 0x06
+#define SMB2_HDR_STATUS 0x08
+#define SMB2_HDR_OPCODE 0x0c
+#define SMB2_HDR_PAD2 0x0e
+#define SMB2_HDR_FLAGS 0x10
+#define SMB2_HDR_UNKNOWN 0x14
+#define SMB2_HDR_SEQNUM 0x18
+#define SMB2_HDR_PID 0x20
+#define SMB2_HDR_TID 0x24
+#define SMB2_HDR_UID 0x28
+#define SMB2_HDR_UID2 0x2c /* whats this? */
+#define SMB2_HDR_SIG 0x30 /* guess ... */
+#define SMB2_HDR_BODY 0x40
+
+/* SMB2 opcodes */
+#define SMB2_OP_NEGPROT 0x00
+#define SMB2_OP_SESSSETUP 0x01
+#define SMB2_OP_TCON 0x03
+#define SMB2_OP_TDIS 0x04
+#define SMB2_OP_CREATE 0x05
+#define SMB2_OP_CLOSE 0x06
+#define SMB2_OP_READ 0x08
+#define SMB2_OP_WRITE 0x09
+#define SMB2_OP_FIND 0x0e
+
+#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */
+
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
new file mode 100644
index 0000000000..a178b35f93
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client transport context management functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "include/dlinklist.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smb2_transport_event_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *private)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(void *ptr)
+{
+ struct smb2_transport *transport = ptr;
+ smb2_transport_dead(transport);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smb2_transport_error(void *private, NTSTATUS status)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ smb2_transport_dead(transport);
+}
+
+static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx)
+{
+ struct smb2_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smb2_transport);
+ if (!transport) return NULL;
+
+ transport->socket = talloc_steal(transport, sock);
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smb2_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smb2_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smb2_transport_event_handler,
+ transport);
+
+ packet_set_serialise(transport->packet, transport->socket->event.fde);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ transport->options.timeout = 30;
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport)
+{
+ smbcli_sock_dead(transport->socket);
+
+ /* kill all pending receives */
+ while (transport->pending_recv) {
+ struct smb2_request *req = transport->pending_recv;
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ uint8_t *buffer, *hdr;
+ int len;
+ struct smb2_request *req;
+ uint64_t seqnum;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+
+ if (len < SMB2_MIN_SIZE) {
+ DEBUG(1,("Discarding smb2 reply of size %d\n", len));
+ goto error;
+ }
+
+ seqnum = BVAL(hdr, SMB2_HDR_SEQNUM);
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->seqnum == seqnum) break;
+ }
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
+ seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.body = hdr+SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.ptr = req->in.body;
+ req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
+
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_ERROR;
+ }
+ dump_data(0, blob.data, blob.length);
+ data_blob_free(&blob);
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smb2_timeout_handler(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private)
+{
+ struct smb2_request *req = talloc_get_type(private, struct smb2_request);
+
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smb2_request_destructor(void *ptr)
+{
+ struct smb2_request *req = talloc_get_type(ptr, struct smb2_request);
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ req->state = SMB2_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.timeout, 0),
+ smb2_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+}