summaryrefslogtreecommitdiffstats
path: root/libcli/smb/tstream_smbXcli_np.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libcli/smb/tstream_smbXcli_np.c1384
1 files changed, 1384 insertions, 0 deletions
diff --git a/libcli/smb/tstream_smbXcli_np.c b/libcli/smb/tstream_smbXcli_np.c
new file mode 100644
index 0000000..8dfc4fb
--- /dev/null
+++ b/libcli/smb/tstream_smbXcli_np.c
@@ -0,0 +1,1384 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2010
+
+ 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 "../lib/util/tevent_ntstatus.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/tsocket/tsocket_internal.h"
+#include "smb_common.h"
+#include "smbXcli_base.h"
+#include "tstream_smbXcli_np.h"
+#include "libcli/security/security.h"
+
+static const struct tstream_context_ops tstream_smbXcli_np_ops;
+
+#define TSTREAM_SMBXCLI_NP_DESIRED_ACCESS ( \
+ SEC_STD_READ_CONTROL | \
+ SEC_FILE_READ_DATA | \
+ SEC_FILE_WRITE_DATA | \
+ SEC_FILE_APPEND_DATA | \
+ SEC_FILE_READ_EA | \
+ SEC_FILE_WRITE_EA | \
+ SEC_FILE_READ_ATTRIBUTE | \
+ SEC_FILE_WRITE_ATTRIBUTE | \
+0)
+
+struct tstream_smbXcli_np_ref;
+
+struct tstream_smbXcli_np {
+ struct smbXcli_conn *conn;
+ struct tstream_smbXcli_np_ref *conn_ref;
+ struct smbXcli_session *session;
+ struct tstream_smbXcli_np_ref *session_ref;
+ struct smbXcli_tcon *tcon;
+ struct tstream_smbXcli_np_ref *tcon_ref;
+ uint16_t pid;
+ unsigned int timeout;
+
+ const char *npipe;
+ bool is_smb1;
+ uint16_t fnum;
+ uint64_t fid_persistent;
+ uint64_t fid_volatile;
+
+ struct {
+ bool active;
+ struct tevent_req *read_req;
+ struct tevent_req *write_req;
+ uint16_t setup[2];
+ } trans;
+
+ struct {
+ off_t ofs;
+ size_t left;
+ uint8_t *buf;
+ } read, write;
+};
+
+struct tstream_smbXcli_np_ref {
+ struct tstream_smbXcli_np *cli_nps;
+};
+
+static int tstream_smbXcli_np_destructor(struct tstream_smbXcli_np *cli_nps)
+{
+ NTSTATUS status;
+
+ if (cli_nps->conn_ref != NULL) {
+ cli_nps->conn_ref->cli_nps = NULL;
+ TALLOC_FREE(cli_nps->conn_ref);
+ }
+
+ if (cli_nps->session_ref != NULL) {
+ cli_nps->session_ref->cli_nps = NULL;
+ TALLOC_FREE(cli_nps->session_ref);
+ }
+
+ if (cli_nps->tcon_ref != NULL) {
+ cli_nps->tcon_ref->cli_nps = NULL;
+ TALLOC_FREE(cli_nps->tcon_ref);
+ }
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ return 0;
+ }
+
+ /*
+ * TODO: do not use a sync call with a destructor!!!
+ *
+ * This only happens, if a caller does talloc_free(),
+ * while the everything was still ok.
+ *
+ * If we get an unexpected failure within a normal
+ * operation, we already do an async cli_close_send()/_recv().
+ *
+ * Once we've fixed all callers to call
+ * tstream_disconnect_send()/_recv(), this will
+ * never be called.
+ *
+ * We use a maximun timeout of 1 second == 1000 msec.
+ */
+ cli_nps->timeout = MIN(cli_nps->timeout, 1000);
+
+ if (cli_nps->is_smb1) {
+ status = smb1cli_close(cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->pid,
+ cli_nps->tcon,
+ cli_nps->session,
+ cli_nps->fnum, UINT32_MAX);
+ } else {
+ status = smb2cli_close(cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->session,
+ cli_nps->tcon,
+ 0, /* flags */
+ cli_nps->fid_persistent,
+ cli_nps->fid_volatile);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("tstream_smbXcli_np_destructor: cli_close "
+ "failed on pipe %s. Error was %s\n",
+ cli_nps->npipe, nt_errstr(status)));
+ }
+ /*
+ * We can't do much on failure
+ */
+ return 0;
+}
+
+static int tstream_smbXcli_np_ref_destructor(struct tstream_smbXcli_np_ref *ref)
+{
+ if (ref->cli_nps == NULL) {
+ return 0;
+ }
+
+ if (ref->cli_nps->conn == NULL) {
+ return 0;
+ }
+
+ ref->cli_nps->conn = NULL;
+ ref->cli_nps->session = NULL;
+ ref->cli_nps->tcon = NULL;
+
+ TALLOC_FREE(ref->cli_nps->conn_ref);
+ TALLOC_FREE(ref->cli_nps->session_ref);
+ TALLOC_FREE(ref->cli_nps->tcon_ref);
+
+ return 0;
+};
+
+static struct tevent_req *tstream_smbXcli_np_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream);
+static int tstream_smbXcli_np_disconnect_recv(struct tevent_req *req,
+ int *perrno);
+
+struct tstream_smbXcli_np_open_state {
+ struct smbXcli_conn *conn;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ uint16_t pid;
+ unsigned int timeout;
+
+ bool is_smb1;
+ uint16_t fnum;
+ uint64_t fid_persistent;
+ uint64_t fid_volatile;
+ const char *npipe;
+};
+
+static void tstream_smbXcli_np_open_done(struct tevent_req *subreq);
+
+struct tevent_req *tstream_smbXcli_np_open_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn *conn,
+ struct smbXcli_session *session,
+ struct smbXcli_tcon *tcon,
+ uint16_t pid,
+ unsigned int timeout,
+ const char *npipe)
+{
+ struct tevent_req *req;
+ struct tstream_smbXcli_np_open_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_smbXcli_np_open_state);
+ if (!req) {
+ return NULL;
+ }
+ state->conn = conn;
+ state->tcon = tcon;
+ state->session = session;
+ state->pid = pid;
+ state->timeout = timeout;
+
+ state->npipe = talloc_strdup(state, npipe);
+ if (tevent_req_nomem(state->npipe, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
+ state->is_smb1 = true;
+ }
+
+ if (state->is_smb1) {
+ const char *smb1_npipe;
+
+ /*
+ * Windows and newer Samba versions allow
+ * the pipe name without leading backslash,
+ * but we should better behave like windows clients
+ */
+ smb1_npipe = talloc_asprintf(state, "\\%s", state->npipe);
+ if (tevent_req_nomem(smb1_npipe, req)) {
+ return tevent_req_post(req, ev);
+ }
+ subreq = smb1cli_ntcreatex_send(state, ev, state->conn,
+ state->timeout,
+ state->pid,
+ state->tcon,
+ state->session,
+ smb1_npipe,
+ 0, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ TSTREAM_SMBXCLI_NP_DESIRED_ACCESS,
+ 0, /* AllocationSize */
+ 0, /* FileAttributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* NTCREATEX_IMPERSONATION_IMPERSONATION */
+ 0); /* SecurityFlags */
+ } else {
+ subreq = smb2cli_create_send(state, ev, state->conn,
+ state->timeout, state->session,
+ state->tcon,
+ npipe,
+ SMB2_OPLOCK_LEVEL_NONE,
+ SMB2_IMPERSONATION_IMPERSONATION,
+ TSTREAM_SMBXCLI_NP_DESIRED_ACCESS,
+ 0, /* file_attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0, /* create_options */
+ NULL); /* blobs */
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, tstream_smbXcli_np_open_done, req);
+
+ return req;
+}
+
+static void tstream_smbXcli_np_open_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_smbXcli_np_open_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_open_state);
+ NTSTATUS status;
+
+ if (state->is_smb1) {
+ status = smb1cli_ntcreatex_recv(subreq, &state->fnum);
+ } else {
+ status = smb2cli_create_recv(subreq,
+ &state->fid_persistent,
+ &state->fid_volatile,
+ NULL, NULL, NULL);
+ }
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS _tstream_smbXcli_np_open_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **_stream,
+ const char *location)
+{
+ struct tstream_smbXcli_np_open_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_open_state);
+ struct tstream_context *stream;
+ struct tstream_smbXcli_np *cli_nps;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ stream = tstream_context_create(mem_ctx,
+ &tstream_smbXcli_np_ops,
+ &cli_nps,
+ struct tstream_smbXcli_np,
+ location);
+ if (!stream) {
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(cli_nps);
+
+ cli_nps->conn_ref = talloc_zero(state->conn,
+ struct tstream_smbXcli_np_ref);
+ if (cli_nps->conn_ref == NULL) {
+ TALLOC_FREE(cli_nps);
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_nps->conn_ref->cli_nps = cli_nps;
+
+ cli_nps->session_ref = talloc_zero(state->session,
+ struct tstream_smbXcli_np_ref);
+ if (cli_nps->session_ref == NULL) {
+ TALLOC_FREE(cli_nps);
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_nps->session_ref->cli_nps = cli_nps;
+
+ cli_nps->tcon_ref = talloc_zero(state->tcon,
+ struct tstream_smbXcli_np_ref);
+ if (cli_nps->tcon_ref == NULL) {
+ TALLOC_FREE(cli_nps);
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_nps->tcon_ref->cli_nps = cli_nps;
+
+ cli_nps->conn = state->conn;
+ cli_nps->session = state->session;
+ cli_nps->tcon = state->tcon;
+ cli_nps->pid = state->pid;
+ cli_nps->timeout = state->timeout;
+ cli_nps->npipe = talloc_move(cli_nps, &state->npipe);
+ cli_nps->is_smb1 = state->is_smb1;
+ cli_nps->fnum = state->fnum;
+ cli_nps->fid_persistent = state->fid_persistent;
+ cli_nps->fid_volatile = state->fid_volatile;
+
+ talloc_set_destructor(cli_nps, tstream_smbXcli_np_destructor);
+ talloc_set_destructor(cli_nps->conn_ref,
+ tstream_smbXcli_np_ref_destructor);
+ talloc_set_destructor(cli_nps->session_ref,
+ tstream_smbXcli_np_ref_destructor);
+ talloc_set_destructor(cli_nps->tcon_ref,
+ tstream_smbXcli_np_ref_destructor);
+
+ cli_nps->trans.active = false;
+ cli_nps->trans.read_req = NULL;
+ cli_nps->trans.write_req = NULL;
+ SSVAL(cli_nps->trans.setup+0, 0, TRANSACT_DCERPCCMD);
+ SSVAL(cli_nps->trans.setup+1, 0, cli_nps->fnum);
+
+ *_stream = stream;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static ssize_t tstream_smbXcli_np_pending_bytes(struct tstream_context *stream)
+{
+ struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream,
+ struct tstream_smbXcli_np);
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ return cli_nps->read.left;
+}
+
+bool tstream_is_smbXcli_np(struct tstream_context *stream)
+{
+ struct tstream_smbXcli_np *cli_nps =
+ talloc_get_type(_tstream_context_data(stream),
+ struct tstream_smbXcli_np);
+
+ if (!cli_nps) {
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS tstream_smbXcli_np_use_trans(struct tstream_context *stream)
+{
+ struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream,
+ struct tstream_smbXcli_np);
+
+ if (cli_nps->trans.read_req) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ if (cli_nps->trans.write_req) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ if (cli_nps->trans.active) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ cli_nps->trans.active = true;
+
+ return NT_STATUS_OK;
+}
+
+unsigned int tstream_smbXcli_np_set_timeout(struct tstream_context *stream,
+ unsigned int timeout)
+{
+ struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream,
+ struct tstream_smbXcli_np);
+ unsigned int old_timeout = cli_nps->timeout;
+
+ cli_nps->timeout = timeout;
+ return old_timeout;
+}
+
+struct tstream_smbXcli_np_writev_state {
+ struct tstream_context *stream;
+ struct tevent_context *ev;
+
+ struct iovec *vector;
+ size_t count;
+
+ int ret;
+
+ struct {
+ int val;
+ const char *location;
+ } error;
+};
+
+static int tstream_smbXcli_np_writev_state_destructor(struct tstream_smbXcli_np_writev_state *state)
+{
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+
+ cli_nps->trans.write_req = NULL;
+
+ return 0;
+}
+
+static void tstream_smbXcli_np_writev_write_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_smbXcli_np_writev_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ const struct iovec *vector,
+ size_t count)
+{
+ struct tevent_req *req;
+ struct tstream_smbXcli_np_writev_state *state;
+ struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream,
+ struct tstream_smbXcli_np);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_smbXcli_np_writev_state);
+ if (!req) {
+ return NULL;
+ }
+ state->stream = stream;
+ state->ev = ev;
+ state->ret = 0;
+
+ talloc_set_destructor(state, tstream_smbXcli_np_writev_state_destructor);
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ tevent_req_error(req, ENOTCONN);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we make a copy of the vector so we can change the structure
+ */
+ state->vector = talloc_array(state, struct iovec, count);
+ if (tevent_req_nomem(state->vector, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memcpy(state->vector, vector, sizeof(struct iovec) * count);
+ state->count = count;
+
+ tstream_smbXcli_np_writev_write_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req);
+static void tstream_smbXcli_np_writev_write_done(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_writev_write_next(struct tevent_req *req)
+{
+ struct tstream_smbXcli_np_writev_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_writev_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *subreq;
+ size_t i;
+ size_t left = 0;
+
+ for (i=0; i < state->count; i++) {
+ left += state->vector[i].iov_len;
+ }
+
+ if (left == 0) {
+ TALLOC_FREE(cli_nps->write.buf);
+ tevent_req_done(req);
+ return;
+ }
+
+ cli_nps->write.ofs = 0;
+ cli_nps->write.left = MIN(left, TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE);
+ cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf,
+ uint8_t, cli_nps->write.left);
+ if (tevent_req_nomem(cli_nps->write.buf, req)) {
+ return;
+ }
+
+ /*
+ * copy the pending buffer first
+ */
+ while (cli_nps->write.left > 0 && state->count > 0) {
+ uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+ size_t len = MIN(cli_nps->write.left, state->vector[0].iov_len);
+
+ memcpy(cli_nps->write.buf + cli_nps->write.ofs, base, len);
+
+ base += len;
+ state->vector[0].iov_base = base;
+ state->vector[0].iov_len -= len;
+
+ cli_nps->write.ofs += len;
+ cli_nps->write.left -= len;
+
+ if (state->vector[0].iov_len == 0) {
+ state->vector += 1;
+ state->count -= 1;
+ }
+
+ state->ret += len;
+ }
+
+ if (cli_nps->trans.active && state->count == 0) {
+ cli_nps->trans.active = false;
+ cli_nps->trans.write_req = req;
+ return;
+ }
+
+ if (cli_nps->trans.read_req && state->count == 0) {
+ cli_nps->trans.write_req = req;
+ tstream_smbXcli_np_readv_trans_start(cli_nps->trans.read_req);
+ return;
+ }
+
+ if (cli_nps->is_smb1) {
+ subreq = smb1cli_writex_send(state, state->ev,
+ cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->pid,
+ cli_nps->tcon,
+ cli_nps->session,
+ cli_nps->fnum,
+ 8, /* 8 means message mode. */
+ cli_nps->write.buf,
+ 0, /* offset */
+ cli_nps->write.ofs); /* size */
+ } else {
+ subreq = smb2cli_write_send(state, state->ev,
+ cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->session,
+ cli_nps->tcon,
+ cli_nps->write.ofs, /* length */
+ 0, /* offset */
+ cli_nps->fid_persistent,
+ cli_nps->fid_volatile,
+ 0, /* remaining_bytes */
+ 0, /* flags */
+ cli_nps->write.buf);
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ tstream_smbXcli_np_writev_write_done,
+ req);
+}
+
+static void tstream_smbXcli_np_writev_disconnect_now(struct tevent_req *req,
+ int error,
+ const char *location);
+
+static void tstream_smbXcli_np_writev_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_smbXcli_np_writev_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_writev_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ uint32_t written;
+ NTSTATUS status;
+
+ if (cli_nps->is_smb1) {
+ status = smb1cli_writex_recv(subreq, &written, NULL);
+ } else {
+ status = smb2cli_write_recv(subreq, &written);
+ }
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tstream_smbXcli_np_writev_disconnect_now(req, EPIPE, __location__);
+ return;
+ }
+
+ if (written != cli_nps->write.ofs) {
+ tstream_smbXcli_np_writev_disconnect_now(req, EIO, __location__);
+ return;
+ }
+
+ tstream_smbXcli_np_writev_write_next(req);
+}
+
+static void tstream_smbXcli_np_writev_disconnect_done(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_writev_disconnect_now(struct tevent_req *req,
+ int error,
+ const char *location)
+{
+ struct tstream_smbXcli_np_writev_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_writev_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *subreq;
+
+ state->error.val = error;
+ state->error.location = location;
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+ return;
+ }
+
+ subreq = tstream_smbXcli_np_disconnect_send(state, state->ev,
+ state->stream);
+ if (subreq == NULL) {
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ tstream_smbXcli_np_writev_disconnect_done,
+ req);
+}
+
+static void tstream_smbXcli_np_writev_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_smbXcli_np_writev_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_writev_state);
+ int error;
+
+ tstream_smbXcli_np_disconnect_recv(subreq, &error);
+ TALLOC_FREE(subreq);
+
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+}
+
+static int tstream_smbXcli_np_writev_recv(struct tevent_req *req,
+ int *perrno)
+{
+ struct tstream_smbXcli_np_writev_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_writev_state);
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->ret;
+ }
+
+ tevent_req_received(req);
+ return ret;
+}
+
+struct tstream_smbXcli_np_readv_state {
+ struct tstream_context *stream;
+ struct tevent_context *ev;
+
+ struct iovec *vector;
+ size_t count;
+
+ int ret;
+
+ struct {
+ struct tevent_immediate *im;
+ } trans;
+
+ struct {
+ int val;
+ const char *location;
+ } error;
+};
+
+static int tstream_smbXcli_np_readv_state_destructor(struct tstream_smbXcli_np_readv_state *state)
+{
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+
+ cli_nps->trans.read_req = NULL;
+
+ return 0;
+}
+
+static void tstream_smbXcli_np_readv_read_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_smbXcli_np_readv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ struct iovec *vector,
+ size_t count)
+{
+ struct tevent_req *req;
+ struct tstream_smbXcli_np_readv_state *state;
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(stream, struct tstream_smbXcli_np);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_smbXcli_np_readv_state);
+ if (!req) {
+ return NULL;
+ }
+ state->stream = stream;
+ state->ev = ev;
+ state->ret = 0;
+
+ talloc_set_destructor(state, tstream_smbXcli_np_readv_state_destructor);
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ tevent_req_error(req, ENOTCONN);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we make a copy of the vector so we can change the structure
+ */
+ state->vector = talloc_array(state, struct iovec, count);
+ if (tevent_req_nomem(state->vector, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memcpy(state->vector, vector, sizeof(struct iovec) * count);
+ state->count = count;
+
+ tstream_smbXcli_np_readv_read_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_readv_read_next(struct tevent_req *req)
+{
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *subreq;
+
+ /*
+ * copy the pending buffer first
+ */
+ while (cli_nps->read.left > 0 && state->count > 0) {
+ uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+ size_t len = MIN(cli_nps->read.left, state->vector[0].iov_len);
+
+ memcpy(base, cli_nps->read.buf + cli_nps->read.ofs, len);
+
+ base += len;
+ state->vector[0].iov_base = base;
+ state->vector[0].iov_len -= len;
+
+ cli_nps->read.ofs += len;
+ cli_nps->read.left -= len;
+
+ if (state->vector[0].iov_len == 0) {
+ state->vector += 1;
+ state->count -= 1;
+ }
+
+ state->ret += len;
+ }
+
+ if (cli_nps->read.left == 0) {
+ TALLOC_FREE(cli_nps->read.buf);
+ }
+
+ if (state->count == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ if (cli_nps->trans.active) {
+ cli_nps->trans.active = false;
+ cli_nps->trans.read_req = req;
+ return;
+ }
+
+ if (cli_nps->trans.write_req) {
+ cli_nps->trans.read_req = req;
+ tstream_smbXcli_np_readv_trans_start(req);
+ return;
+ }
+
+ if (cli_nps->is_smb1) {
+ subreq = smb1cli_readx_send(state, state->ev,
+ cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->pid,
+ cli_nps->tcon,
+ cli_nps->session,
+ cli_nps->fnum,
+ 0, /* offset */
+ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE);
+ } else {
+ subreq = smb2cli_read_send(state, state->ev,
+ cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->session,
+ cli_nps->tcon,
+ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, /* length */
+ 0, /* offset */
+ cli_nps->fid_persistent,
+ cli_nps->fid_volatile,
+ 0, /* minimum_count */
+ 0); /* remaining_bytes */
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ tstream_smbXcli_np_readv_read_done,
+ req);
+}
+
+static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req)
+{
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *subreq;
+
+ state->trans.im = tevent_create_immediate(state);
+ if (tevent_req_nomem(state->trans.im, req)) {
+ return;
+ }
+
+ if (cli_nps->is_smb1) {
+ subreq = smb1cli_trans_send(state, state->ev,
+ cli_nps->conn, SMBtrans,
+ 0, 0, /* *_flags */
+ 0, 0, /* *_flags2 */
+ cli_nps->timeout,
+ cli_nps->pid,
+ cli_nps->tcon,
+ cli_nps->session,
+ "\\PIPE\\",
+ 0, 0, 0,
+ cli_nps->trans.setup, 2,
+ 0,
+ NULL, 0, 0,
+ cli_nps->write.buf,
+ cli_nps->write.ofs,
+ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE);
+ } else {
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ in_input_buffer = data_blob_const(cli_nps->write.buf,
+ cli_nps->write.ofs);
+
+ subreq = smb2cli_ioctl_send(state, state->ev,
+ cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->session,
+ cli_nps->tcon,
+ cli_nps->fid_persistent,
+ cli_nps->fid_volatile,
+ FSCTL_NAMED_PIPE_READ_WRITE,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ /* in_max_output_length */
+ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE,
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ tstream_smbXcli_np_readv_trans_done,
+ req);
+}
+
+static void tstream_smbXcli_np_readv_disconnect_now(struct tevent_req *req,
+ int error,
+ const char *location);
+static void tstream_smbXcli_np_readv_trans_next(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data);
+
+static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream, struct tstream_smbXcli_np);
+ uint8_t *rcvbuf;
+ uint32_t received;
+ NTSTATUS status;
+
+ if (cli_nps->is_smb1) {
+ status = smb1cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
+ NULL, 0, NULL,
+ &rcvbuf, 0, &received);
+ } else {
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+
+ /* Note that rcvbuf is not a talloc pointer here */
+ rcvbuf = out_output_buffer.data;
+ received = out_output_buffer.length;
+ }
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ /*
+ * STATUS_BUFFER_OVERFLOW means that there's
+ * more data to read when the named pipe is used
+ * in message mode (which is the case here).
+ *
+ * But we hide this from the caller.
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
+ return;
+ }
+
+ if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) {
+ tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__);
+ return;
+ }
+
+ if (received == 0) {
+ tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
+ return;
+ }
+
+ cli_nps->read.ofs = 0;
+ cli_nps->read.left = received;
+ cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
+ if (cli_nps->read.buf == NULL) {
+ TALLOC_FREE(subreq);
+ tevent_req_oom(req);
+ return;
+ }
+ memcpy(cli_nps->read.buf, rcvbuf, received);
+
+ if (cli_nps->trans.write_req == NULL) {
+ tstream_smbXcli_np_readv_read_next(req);
+ return;
+ }
+
+ tevent_schedule_immediate(state->trans.im, state->ev,
+ tstream_smbXcli_np_readv_trans_next, req);
+
+ tevent_req_done(cli_nps->trans.write_req);
+}
+
+static void tstream_smbXcli_np_readv_trans_next(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(private_data,
+ struct tevent_req);
+
+ tstream_smbXcli_np_readv_read_next(req);
+}
+
+static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream, struct tstream_smbXcli_np);
+ uint8_t *rcvbuf;
+ uint32_t received;
+ NTSTATUS status;
+
+ /*
+ * We must free subreq in this function as there is
+ * a timer event attached to it.
+ */
+
+ if (cli_nps->is_smb1) {
+ status = smb1cli_readx_recv(subreq, &received, &rcvbuf);
+ } else {
+ status = smb2cli_read_recv(subreq, state, &rcvbuf, &received);
+ }
+ /*
+ * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a
+ * child of that.
+ */
+ if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ /*
+ * STATUS_BUFFER_OVERFLOW means that there's
+ * more data to read when the named pipe is used
+ * in message mode (which is the case here).
+ *
+ * But we hide this from the caller.
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(subreq);
+ tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
+ return;
+ }
+
+ if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) {
+ TALLOC_FREE(subreq);
+ tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__);
+ return;
+ }
+
+ if (received == 0) {
+ TALLOC_FREE(subreq);
+ tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
+ return;
+ }
+
+ cli_nps->read.ofs = 0;
+ cli_nps->read.left = received;
+ cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
+ if (cli_nps->read.buf == NULL) {
+ TALLOC_FREE(subreq);
+ tevent_req_oom(req);
+ return;
+ }
+ memcpy(cli_nps->read.buf, rcvbuf, received);
+ TALLOC_FREE(subreq);
+
+ tstream_smbXcli_np_readv_read_next(req);
+}
+
+static void tstream_smbXcli_np_readv_disconnect_done(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_readv_error(struct tevent_req *req);
+
+static void tstream_smbXcli_np_readv_disconnect_now(struct tevent_req *req,
+ int error,
+ const char *location)
+{
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *subreq;
+
+ state->error.val = error;
+ state->error.location = location;
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ /* return the original error */
+ tstream_smbXcli_np_readv_error(req);
+ return;
+ }
+
+ subreq = tstream_smbXcli_np_disconnect_send(state, state->ev,
+ state->stream);
+ if (subreq == NULL) {
+ /* return the original error */
+ tstream_smbXcli_np_readv_error(req);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ tstream_smbXcli_np_readv_disconnect_done,
+ req);
+}
+
+static void tstream_smbXcli_np_readv_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ int error;
+
+ tstream_smbXcli_np_disconnect_recv(subreq, &error);
+ TALLOC_FREE(subreq);
+
+ tstream_smbXcli_np_readv_error(req);
+}
+
+static void tstream_smbXcli_np_readv_error_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data);
+
+static void tstream_smbXcli_np_readv_error(struct tevent_req *req)
+{
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_readv_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream,
+ struct tstream_smbXcli_np);
+
+ if (cli_nps->trans.write_req == NULL) {
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+ return;
+ }
+
+ if (state->trans.im == NULL) {
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+ return;
+ }
+
+ tevent_schedule_immediate(state->trans.im, state->ev,
+ tstream_smbXcli_np_readv_error_trigger, req);
+
+ /* return the original error for writev */
+ _tevent_req_error(cli_nps->trans.write_req,
+ state->error.val, state->error.location);
+}
+
+static void tstream_smbXcli_np_readv_error_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(private_data,
+ struct tevent_req);
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_smbXcli_np_readv_state);
+
+ /* return the original error */
+ _tevent_req_error(req, state->error.val, state->error.location);
+}
+
+static int tstream_smbXcli_np_readv_recv(struct tevent_req *req,
+ int *perrno)
+{
+ struct tstream_smbXcli_np_readv_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_readv_state);
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->ret;
+ }
+
+ tevent_req_received(req);
+ return ret;
+}
+
+struct tstream_smbXcli_np_disconnect_state {
+ struct tstream_context *stream;
+ struct tevent_req *subreq;
+};
+
+static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq);
+static void tstream_smbXcli_np_disconnect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+
+static struct tevent_req *tstream_smbXcli_np_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream)
+{
+ struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream,
+ struct tstream_smbXcli_np);
+ struct tevent_req *req;
+ struct tstream_smbXcli_np_disconnect_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_smbXcli_np_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->stream = stream;
+
+ if (!smbXcli_conn_is_connected(cli_nps->conn)) {
+ tevent_req_error(req, ENOTCONN);
+ return tevent_req_post(req, ev);
+ }
+
+ if (cli_nps->is_smb1) {
+ subreq = smb1cli_close_send(state, ev, cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->pid,
+ cli_nps->tcon,
+ cli_nps->session,
+ cli_nps->fnum, UINT32_MAX);
+ } else {
+ subreq = smb2cli_close_send(state, ev, cli_nps->conn,
+ cli_nps->timeout,
+ cli_nps->session,
+ cli_nps->tcon,
+ 0, /* flags */
+ cli_nps->fid_persistent,
+ cli_nps->fid_volatile);
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, tstream_smbXcli_np_disconnect_done, req);
+ state->subreq = subreq;
+
+ tevent_req_set_cleanup_fn(req, tstream_smbXcli_np_disconnect_cleanup);
+
+ /*
+ * Make sure we don't send any requests anymore.
+ */
+ cli_nps->conn = NULL;
+
+ return req;
+}
+
+static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct tstream_smbXcli_np_disconnect_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_disconnect_state);
+ struct tstream_smbXcli_np *cli_nps =
+ tstream_context_data(state->stream, struct tstream_smbXcli_np);
+ NTSTATUS status;
+
+ state->subreq = NULL;
+
+ if (cli_nps->is_smb1) {
+ status = smb1cli_close_recv(subreq);
+ } else {
+ status = smb2cli_close_recv(subreq);
+ }
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_error(req, EPIPE);
+ return;
+ }
+
+ cli_nps->conn = NULL;
+ cli_nps->session = NULL;
+ cli_nps->tcon = NULL;
+
+ tevent_req_done(req);
+}
+
+static void tstream_smbXcli_np_disconnect_free(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_disconnect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct tstream_smbXcli_np_disconnect_state *state =
+ tevent_req_data(req, struct tstream_smbXcli_np_disconnect_state);
+ struct tstream_smbXcli_np *cli_nps = NULL;
+
+ if (state->subreq == NULL) {
+ return;
+ }
+
+ cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np);
+
+ if (cli_nps->tcon == NULL) {
+ return;
+ }
+
+ /*
+ * We're no longer interested in the result
+ * any more, but need to make sure that the close
+ * request arrives at the server if the smb connection,
+ * session and tcon are still alive.
+ *
+ * We move the low level request to the tcon,
+ * which means that it stays as long as the tcon
+ * is available.
+ */
+ talloc_steal(cli_nps->tcon, state->subreq);
+ tevent_req_set_callback(state->subreq,
+ tstream_smbXcli_np_disconnect_free,
+ NULL);
+ state->subreq = NULL;
+
+ cli_nps->conn = NULL;
+ cli_nps->session = NULL;
+ cli_nps->tcon = NULL;
+}
+
+static void tstream_smbXcli_np_disconnect_free(struct tevent_req *subreq)
+{
+ TALLOC_FREE(subreq);
+}
+
+static int tstream_smbXcli_np_disconnect_recv(struct tevent_req *req,
+ int *perrno)
+{
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+
+ tevent_req_received(req);
+ return ret;
+}
+
+static const struct tstream_context_ops tstream_smbXcli_np_ops = {
+ .name = "smbXcli_np",
+
+ .pending_bytes = tstream_smbXcli_np_pending_bytes,
+
+ .readv_send = tstream_smbXcli_np_readv_send,
+ .readv_recv = tstream_smbXcli_np_readv_recv,
+
+ .writev_send = tstream_smbXcli_np_writev_send,
+ .writev_recv = tstream_smbXcli_np_writev_recv,
+
+ .disconnect_send = tstream_smbXcli_np_disconnect_send,
+ .disconnect_recv = tstream_smbXcli_np_disconnect_recv,
+};