STEP:smb_transport: TODO: libcli/smb: add smb_transport*
authorStefan Metzmacher <metze@samba.org>
Fri, 21 Sep 2012 20:17:56 +0000 (22:17 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Jun 2018 12:35:00 +0000 (14:35 +0200)
This is a generic abstraction for a transport that can be used
by our SMB1/2/3 code.

TODO: don't return the NBT header in smb_transport_read_pdu_recv
(smb_transport_tcp_read_pdu_recv), but that needs more work in
the callers...

libcli/smb/smb_transport.c [new file with mode: 0644]
libcli/smb/smb_transport.h [new file with mode: 0644]
libcli/smb/smb_transport_tcp.c [new file with mode: 0644]
libcli/smb/wscript

diff --git a/libcli/smb/smb_transport.c b/libcli/smb/smb_transport.c
new file mode 100644 (file)
index 0000000..436eb31
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async SMB client requests
+   Copyright (C) Volker Lendecke 2008
+
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "../util/tevent_ntstatus.h"
+#include "libcli/smb/smb_transport.h"
+
+struct smb_transport {
+       const char *location;
+       const struct smb_transport_ops *ops;
+       void *private_data;
+
+       struct tevent_req *write_pdu_req;
+       struct tevent_req *read_pdu_req;
+};
+
+void *_smb_transport_data(struct smb_transport *transport)
+{
+       return transport->private_data;
+}
+
+static int smb_transport_destructor(struct smb_transport *transport)
+{
+       if (transport->read_pdu_req) {
+               tevent_req_received(transport->read_pdu_req);
+       }
+
+       if (transport->write_pdu_req) {
+               tevent_req_received(transport->write_pdu_req);
+       }
+
+       return 0;
+}
+
+struct smb_transport *_smb_transport_create(TALLOC_CTX *mem_ctx,
+                                       const struct smb_transport_ops *ops,
+                                       void *pstate,
+                                       size_t psize,
+                                       const char *type,
+                                       const char *location)
+{
+       struct smb_transport *transport;
+       void **ppstate = (void **)pstate;
+       void *state;
+
+       transport = talloc_zero(mem_ctx, struct smb_transport);
+       if (transport == NULL) {
+               return NULL;
+       }
+       transport->location     = location;
+       transport->ops          = ops;
+
+       state = talloc_zero_size(transport, psize);
+       if (state == NULL) {
+               talloc_free(transport);
+               return NULL;
+       }
+       talloc_set_name_const(state, type);
+
+       transport->private_data = state;
+
+       talloc_set_destructor(transport, smb_transport_destructor);
+
+       *ppstate = state;
+       return transport;
+}
+
+struct smb_transport_write_pdu_state {
+       const struct smb_transport_ops *ops;
+       struct smb_transport *transport;
+};
+
+static int smb_transport_write_pdu_destructor(struct smb_transport_write_pdu_state *state)
+{
+       if (state->transport) {
+               state->transport->write_pdu_req = NULL;
+       }
+
+       return 0;
+}
+
+static void smb_transport_write_pdu_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_transport_write_pdu_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct smb_transport *transport,
+                                      const struct iovec *vector,
+                                      size_t count)
+{
+       struct tevent_req *req;
+       struct smb_transport_write_pdu_state *state;
+       struct tevent_req *subreq;
+       int to_write = 0;
+       size_t i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_transport_write_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ops = transport->ops;
+       state->transport = transport;
+
+       /* first check if the input is ok */
+#ifdef IOV_MAX
+       if (count > IOV_MAX) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+#endif
+
+       for (i=0; i < count; i++) {
+               int tmp = to_write;
+               tmp += vector[i].iov_len;
+
+               if (tmp < to_write) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return tevent_req_post(req, ev);
+               }
+
+               to_write = tmp;
+       }
+
+       if (to_write == 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       if (transport->write_pdu_req) {
+               tevent_req_nterror(req, NT_STATUS_PIPE_BUSY);;
+               return tevent_req_post(req, ev);
+       }
+       transport->write_pdu_req = req;
+
+       talloc_set_destructor(state, smb_transport_write_pdu_destructor);
+
+       subreq = state->ops->write_pdu_send(state, ev, transport, vector, count);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smb_transport_write_pdu_done, req);
+
+       return req;
+}
+
+static void smb_transport_write_pdu_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb_transport_write_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_transport_write_pdu_state);
+       NTSTATUS status;
+
+       status = state->ops->write_pdu_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS smb_transport_write_pdu_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct smb_transport_read_pdu_state {
+       const struct smb_transport_ops *ops;
+       struct smb_transport *transport;
+       struct iovec recv_iov;
+};
+
+static int smb_transport_read_pdu_destructor(struct smb_transport_read_pdu_state *state)
+{
+       if (state->transport) {
+               state->transport->read_pdu_req = NULL;
+       }
+
+       return 0;
+}
+
+static void smb_transport_read_pdu_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_transport_read_pdu_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct smb_transport *transport)
+{
+       struct tevent_req *req;
+       struct smb_transport_read_pdu_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_transport_read_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ops = transport->ops;
+       state->transport = transport;
+
+       if (transport->read_pdu_req) {
+               tevent_req_nterror(req, NT_STATUS_PIPE_BUSY);
+               return tevent_req_post(req, ev);
+       }
+       transport->read_pdu_req = req;
+
+       talloc_set_destructor(state, smb_transport_read_pdu_destructor);
+
+       subreq = state->ops->read_pdu_send(state, ev, transport);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smb_transport_read_pdu_done, req);
+
+       return req;
+}
+
+static void smb_transport_read_pdu_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb_transport_read_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_transport_read_pdu_state);
+       NTSTATUS status;
+
+       status = state->ops->read_pdu_recv(subreq, state, &state->recv_iov);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS smb_transport_read_pdu_recv(struct tevent_req *req,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct iovec *vector)
+{
+       struct smb_transport_read_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_transport_read_pdu_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       vector->iov_base = talloc_move(mem_ctx, &state->recv_iov.iov_base);
+       vector->iov_len = state->recv_iov.iov_len;
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
diff --git a/libcli/smb/smb_transport.h b/libcli/smb/smb_transport.h
new file mode 100644 (file)
index 0000000..4ea1de3
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async SMB client requests
+   Copyright (C) Volker Lendecke 2008
+
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBSMB_SMB_TRANSPORT_H
+#define __LIBSMB_SMB_TRANSPORT_H
+
+struct tevent_context;
+struct tevent_req;
+struct smb_transport;
+struct iovec;
+
+struct smb_transport_ops {
+       const char *name;
+
+       struct tevent_req *(*write_pdu_send)(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct smb_transport *transport,
+                                            const struct iovec *vector,
+                                            size_t count);
+       NTSTATUS (*write_pdu_recv)(struct tevent_req *req);
+
+       struct tevent_req *(*read_pdu_send)(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct smb_transport *transport);
+       NTSTATUS (*read_pdu_recv)(struct tevent_req *req,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct iovec *vector);
+};
+
+struct smb_transport *_smb_transport_create(TALLOC_CTX *mem_ctx,
+                                       const struct smb_transport_ops *ops,
+                                       void *pstate,
+                                       size_t psize,
+                                       const char *type,
+                                       const char *location);
+#define smb_transport_create(mem_ctx, ops, state, type, location) \
+       _smb_transport_create(mem_ctx, ops, state, sizeof(type), \
+                             #type, location)
+
+void *_smb_transport_data(struct smb_transport *transport);
+#define smb_transport_data(_req, _type) \
+       talloc_get_type_abort(_smb_transport_data(_req), _type)
+
+struct tevent_req *smb_transport_write_pdu_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct smb_transport *transport,
+                                               const struct iovec *vector,
+                                               size_t count);
+NTSTATUS smb_transport_write_pdu_recv(struct tevent_req *req);
+
+struct tevent_req *smb_transport_read_pdu_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct smb_transport *transport);
+NTSTATUS smb_transport_read_pdu_recv(struct tevent_req *req,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct iovec *vector);
+
+NTSTATUS _smb_transport_tcp_existing(TALLOC_CTX *mem_ctx,
+                                int fd, size_t max_pdu_size,
+                                struct smb_transport **_transport,
+                                const char *location);
+#define smb_transport_tcp_existing(mem_ctx, fd, max_pdu_size, transport) \
+       _smb_transport_tcp_existing(mem_ctx, fd, max_pdu_size, transport, __location__)
+
+#endif
diff --git a/libcli/smb/smb_transport_tcp.c b/libcli/smb/smb_transport_tcp.c
new file mode 100644 (file)
index 0000000..6fa3c69
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async SMB client requests
+   Copyright (C) Volker Lendecke 2008
+
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "../util/tevent_ntstatus.h"
+#include "libcli/smb/smb_transport.h"
+#include "libcli/smb/smb_common.h"
+#include "../lib/async_req/async_sock.h"
+#include "../libcli/smb/read_smb.h"
+
+struct smb_transport_tcp {
+       bool nbt;
+       size_t max_pdu_size;
+       int write_fd;
+       int read_fd;
+};
+
+static int smb_transport_tcp_destructor(struct smb_transport_tcp *ttcp)
+{
+       if (ttcp->write_fd != -1) {
+               close(ttcp->write_fd);
+               ttcp->write_fd = -1;
+       }
+
+       if (ttcp->read_fd != -1) {
+               close(ttcp->read_fd);
+               ttcp->read_fd = -1;
+       }
+
+       return 0;
+}
+
+struct smb_transport_tcp_write_pdu_state {
+       uint8_t hdr[NBT_HDR_SIZE];
+       struct iovec *vector;
+       size_t count;
+};
+
+static void smb_transport_tcp_write_pdu_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_transport_tcp_write_pdu_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct smb_transport *transport,
+                                      const struct iovec *vector,
+                                      size_t count)
+{
+       struct tevent_req *req;
+       struct smb_transport_tcp_write_pdu_state *state;
+       struct smb_transport_tcp *ttcp =
+               smb_transport_data(transport,
+               struct smb_transport_tcp);
+       struct tevent_req *subreq;
+       int to_write = 0;
+       size_t i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_transport_tcp_write_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       /* first check if the input is ok */
+#ifdef IOV_MAX
+       if (count > IOV_MAX) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+#endif
+
+       for (i=0; i < count; i++) {
+               int tmp = to_write;
+               tmp += vector[i].iov_len;
+
+               if (tmp < to_write) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return tevent_req_post(req, ev);
+               }
+
+               to_write = tmp;
+       }
+
+       if (to_write == 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       if (to_write > ttcp->max_pdu_size) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       _smb_setlen_tcp(state->hdr, to_write);
+
+       /* we make a copy of the vector so that we can modify it */
+       state->vector = talloc_array(state, struct iovec, 1 + count);
+       if (tevent_req_nomem(state->vector, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->vector[0].iov_base = (void *)state->hdr;
+       state->vector[0].iov_len = sizeof(state->hdr);
+       memcpy(&state->vector[1], vector, sizeof(struct iovec)*count);
+       state->count    = 1 + count;
+
+       subreq = writev_send(state, ev, NULL, ttcp->write_fd,
+                            false, state->vector, state->count);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smb_transport_tcp_write_pdu_done, req);
+
+       return req;
+}
+
+static void smb_transport_tcp_write_pdu_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       ssize_t ret;
+       int sys_errno;
+
+       ret = writev_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               NTSTATUS status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS smb_transport_tcp_write_pdu_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct smb_transport_tcp_read_pdu_state {
+       uint8_t *inbuf;
+};
+
+static void smb_transport_tcp_read_pdu_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_transport_tcp_read_pdu_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct smb_transport *transport)
+{
+       struct tevent_req *req;
+       struct smb_transport_tcp_read_pdu_state *state;
+       struct smb_transport_tcp *ttcp =
+               smb_transport_data(transport,
+               struct smb_transport_tcp);
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_transport_tcp_read_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       subreq = read_smb_send(state, ev, ttcp->read_fd);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smb_transport_tcp_read_pdu_done, req);
+
+       return req;
+}
+
+static void smb_transport_tcp_read_pdu_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb_transport_tcp_read_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_transport_tcp_read_pdu_state);
+       ssize_t ret;
+       int sys_errno;
+
+       ret = read_smb_recv(subreq, state, &state->inbuf, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               NTSTATUS status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS smb_transport_tcp_read_pdu_recv(struct tevent_req *req,
+                                               TALLOC_CTX *mem_ctx,
+                                               struct iovec *vector)
+{
+       struct smb_transport_tcp_read_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_transport_tcp_read_pdu_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       vector->iov_len = NBT_HDR_SIZE + smb_len_tcp(state->inbuf);
+       vector->iov_base = talloc_move(mem_ctx, &state->inbuf);
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+static const struct smb_transport_ops smb_transport_tcp_ops = {
+       .name                   = "tcp/nbt",
+
+       .read_pdu_send          = smb_transport_tcp_read_pdu_send,
+       .read_pdu_recv          = smb_transport_tcp_read_pdu_recv,
+
+       .write_pdu_send         = smb_transport_tcp_write_pdu_send,
+       .write_pdu_recv         = smb_transport_tcp_write_pdu_recv,
+};
+
+NTSTATUS _smb_transport_tcp_existing(TALLOC_CTX *mem_ctx,
+                                int fd, size_t max_pdu_size,
+                                struct smb_transport **_transport,
+                                const char *location)
+{
+       struct smb_transport *transport;
+       struct smb_transport_tcp *ttcp;
+       int write_fd;
+
+       write_fd = dup(fd);
+       if (write_fd == -1) {
+               NTSTATUS status;
+
+               if (errno == 0) {
+                       errno = EBADF;
+               }
+
+               status = map_nt_error_from_unix_common(errno);
+               return status;
+       }
+
+       transport = smb_transport_create(mem_ctx,
+                                        &smb_transport_tcp_ops,
+                                        &ttcp,
+                                        struct smb_transport_tcp,
+                                        location);
+       if (transport == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       ttcp->max_pdu_size = max_pdu_size;
+       ttcp->read_fd = fd;
+       ttcp->write_fd = write_fd;
+
+       talloc_set_destructor(ttcp, smb_transport_tcp_destructor);
+
+       *_transport = transport;
+       return NT_STATUS_OK;
+}
index 53a5c2139538e7fea8faef626ddd7a0492a8395d..262e0fdb2e25c43dc921be464028645527b0f5ac 100644 (file)
@@ -5,12 +5,15 @@ def build(bld):
     bld.SAMBA_LIBRARY('smb_transport',
         source='''
             read_smb.c
+            smb_transport.c
+            smb_transport_tcp.c
         ''',
         deps='LIBASYNC_REQ',
         public_deps='talloc tevent',
         private_library=True,
         private_headers='''
             read_smb.h
+            smb_transport.h
         ''',
         )