--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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;
+}
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
''',
)