diff options
author | Stefan Metzmacher <metze@samba.org> | 2013-09-13 15:50:10 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2014-01-07 08:37:42 +0100 |
commit | 024fc73047ffb6bb25dfdfbd99dbd7f490eaaa13 (patch) | |
tree | c5b43f853ca3c6d32b53a4896dfc95e17d158add /libcli/smb | |
parent | acbd12adf90830070db7e5aec088aa3051dfaa63 (diff) | |
download | samba-024fc73047ffb6bb25dfdfbd99dbd7f490eaaa13.tar.gz samba-024fc73047ffb6bb25dfdfbd99dbd7f490eaaa13.tar.xz samba-024fc73047ffb6bb25dfdfbd99dbd7f490eaaa13.zip |
libcli/smb: move source3/libsmb/cli_np_tstream.c to tstream_smbXcli_np.c
This code is generic enough to have it in the top level now.
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Diffstat (limited to 'libcli/smb')
-rw-r--r-- | libcli/smb/tstream_smbXcli_np.c | 1336 | ||||
-rw-r--r-- | libcli/smb/tstream_smbXcli_np.h | 51 | ||||
-rwxr-xr-x | libcli/smb/wscript | 2 |
3 files changed, 1389 insertions, 0 deletions
diff --git a/libcli/smb/tstream_smbXcli_np.c b/libcli/smb/tstream_smbXcli_np.c new file mode 100644 index 0000000000..2faad46ead --- /dev/null +++ b/libcli/smb/tstream_smbXcli_np.c @@ -0,0 +1,1336 @@ +/* + 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_cli_np_ops; + +/* + * Windows uses 4280 (the max xmit/recv size negotiated on DCERPC). + * This is fits into the max_xmit negotiated at the SMB layer. + * + * On the sending side they may use SMBtranss if the request does not + * fit into a single SMBtrans call. + * + * Windows uses 1024 as max data size of a SMBtrans request and then + * possibly reads the rest of the DCERPC fragment (up to 3256 bytes) + * via a SMBreadX. + * + * For now we just ask for the full 4280 bytes (max data size) in the SMBtrans + * request to get the whole fragment at once (like samba 3.5.x and below did. + * + * It is important that we use do SMBwriteX with the size of a full fragment, + * otherwise we may get NT_STATUS_PIPE_BUSY on the SMBtrans request + * from NT4 servers. (See bug #8195) + */ +#define TSTREAM_CLI_NP_MAX_BUF_SIZE 4280 + +#define TSTREAM_CLI_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_cli_np_ref; + +struct tstream_cli_np { + struct tstream_cli_np_ref *ref; + struct smbXcli_conn *conn; + struct smbXcli_session *session; + struct smbXcli_tcon *tcon; + 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_cli_np_ref { + struct tstream_cli_np *cli_nps; +}; + +static int tstream_cli_np_destructor(struct tstream_cli_np *cli_nps) +{ + NTSTATUS status; + + if (cli_nps->ref != NULL) { + cli_nps->ref->cli_nps = NULL; + TALLOC_FREE(cli_nps->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. + */ + 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_cli_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_cli_np_ref_destructor(struct tstream_cli_np_ref *ref) +{ + if (ref->cli_nps == NULL) { + return 0; + } + + ref->cli_nps->conn = NULL; + ref->cli_nps->session = NULL; + ref->cli_nps->tcon = NULL; + ref->cli_nps->ref = NULL; + + return 0; +}; + +struct tstream_cli_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_cli_np_open_done(struct tevent_req *subreq); + +struct tevent_req *tstream_cli_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_cli_np_open_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_cli_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_CLI_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_CLI_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_cli_np_open_done, req); + + return req; +} + +static void tstream_cli_np_open_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_open_state *state = + tevent_req_data(req, struct tstream_cli_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); + } + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); +} + +NTSTATUS _tstream_cli_np_open_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct tstream_context **_stream, + const char *location) +{ + struct tstream_cli_np_open_state *state = + tevent_req_data(req, struct tstream_cli_np_open_state); + struct tstream_context *stream; + struct tstream_cli_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_cli_np_ops, + &cli_nps, + struct tstream_cli_np, + location); + if (!stream) { + tevent_req_received(req); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(cli_nps); + + cli_nps->ref = talloc_zero(state->conn, struct tstream_cli_np_ref); + if (cli_nps->ref == NULL) { + TALLOC_FREE(cli_nps); + tevent_req_received(req); + return NT_STATUS_NO_MEMORY; + } + cli_nps->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_cli_np_destructor); + talloc_set_destructor(cli_nps->ref, tstream_cli_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_cli_np_pending_bytes(struct tstream_context *stream) +{ + struct tstream_cli_np *cli_nps = tstream_context_data(stream, + struct tstream_cli_np); + + if (!smbXcli_conn_is_connected(cli_nps->conn)) { + errno = ENOTCONN; + return -1; + } + + return cli_nps->read.left; +} + +bool tstream_is_cli_np(struct tstream_context *stream) +{ + struct tstream_cli_np *cli_nps = + talloc_get_type(_tstream_context_data(stream), + struct tstream_cli_np); + + if (!cli_nps) { + return false; + } + + return true; +} + +NTSTATUS tstream_cli_np_use_trans(struct tstream_context *stream) +{ + struct tstream_cli_np *cli_nps = tstream_context_data(stream, + struct tstream_cli_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_cli_np_set_timeout(struct tstream_context *stream, + unsigned int timeout) +{ + struct tstream_cli_np *cli_nps = tstream_context_data(stream, + struct tstream_cli_np); + unsigned int old_timeout = cli_nps->timeout; + + cli_nps->timeout = timeout; + return old_timeout; +} + +struct tstream_cli_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_cli_np_writev_state_destructor(struct tstream_cli_np_writev_state *state) +{ + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_np); + + cli_nps->trans.write_req = NULL; + + return 0; +} + +static void tstream_cli_np_writev_write_next(struct tevent_req *req); + +static struct tevent_req *tstream_cli_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_cli_np_writev_state *state; + struct tstream_cli_np *cli_nps = tstream_context_data(stream, + struct tstream_cli_np); + + req = tevent_req_create(mem_ctx, &state, + struct tstream_cli_np_writev_state); + if (!req) { + return NULL; + } + state->stream = stream; + state->ev = ev; + state->ret = 0; + + talloc_set_destructor(state, tstream_cli_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_cli_np_writev_write_next(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void tstream_cli_np_readv_trans_start(struct tevent_req *req); +static void tstream_cli_np_writev_write_done(struct tevent_req *subreq); + +static void tstream_cli_np_writev_write_next(struct tevent_req *req) +{ + struct tstream_cli_np_writev_state *state = + tevent_req_data(req, + struct tstream_cli_np_writev_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_CLI_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_cli_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_cli_np_writev_write_done, + req); +} + +static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req, + int error, + const char *location); + +static void tstream_cli_np_writev_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_writev_state *state = + tevent_req_data(req, struct tstream_cli_np_writev_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_cli_np_writev_disconnect_now(req, EIO, __location__); + return; + } + + if (written != cli_nps->write.ofs) { + tstream_cli_np_writev_disconnect_now(req, EIO, __location__); + return; + } + + tstream_cli_np_writev_write_next(req); +} + +static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq); + +static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req, + int error, + const char *location) +{ + struct tstream_cli_np_writev_state *state = + tevent_req_data(req, + struct tstream_cli_np_writev_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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; + } + + if (cli_nps->is_smb1) { + subreq = smb1cli_close_send(state, 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, 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 (subreq == NULL) { + /* return the original error */ + _tevent_req_error(req, state->error.val, state->error.location); + return; + } + tevent_req_set_callback(subreq, + tstream_cli_np_writev_disconnect_done, + req); +} + +static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_writev_state *state = + tevent_req_data(req, struct tstream_cli_np_writev_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, struct tstream_cli_np); + + if (cli_nps->is_smb1) { + smb1cli_close_recv(subreq); + } else { + smb2cli_close_recv(subreq); + } + TALLOC_FREE(subreq); + + cli_nps->conn = NULL; + cli_nps->tcon = NULL; + cli_nps->session = NULL; + + /* return the original error */ + _tevent_req_error(req, state->error.val, state->error.location); +} + +static int tstream_cli_np_writev_recv(struct tevent_req *req, + int *perrno) +{ + struct tstream_cli_np_writev_state *state = + tevent_req_data(req, + struct tstream_cli_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_cli_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_cli_np_readv_state_destructor(struct tstream_cli_np_readv_state *state) +{ + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_np); + + cli_nps->trans.read_req = NULL; + + return 0; +} + +static void tstream_cli_np_readv_read_next(struct tevent_req *req); + +static struct tevent_req *tstream_cli_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_cli_np_readv_state *state; + struct tstream_cli_np *cli_nps = + tstream_context_data(stream, struct tstream_cli_np); + + req = tevent_req_create(mem_ctx, &state, + struct tstream_cli_np_readv_state); + if (!req) { + return NULL; + } + state->stream = stream; + state->ev = ev; + state->ret = 0; + + talloc_set_destructor(state, tstream_cli_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_cli_np_readv_read_next(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void tstream_cli_np_readv_read_done(struct tevent_req *subreq); + +static void tstream_cli_np_readv_read_next(struct tevent_req *req) +{ + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, + struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_cli_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_CLI_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_CLI_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_cli_np_readv_read_done, + req); +} + +static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq); + +static void tstream_cli_np_readv_trans_start(struct tevent_req *req) +{ + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, + struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_CLI_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_CLI_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_cli_np_readv_trans_done, + req); +} + +static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req, + int error, + const char *location); +static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); + +static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, struct tstream_cli_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, NT_STATUS_BUFFER_TOO_SMALL)) { + status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + tstream_cli_np_readv_disconnect_now(req, EIO, __location__); + return; + } + + if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) { + tstream_cli_np_readv_disconnect_now(req, EIO, __location__); + return; + } + + if (received == 0) { + tstream_cli_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_nomem(cli_nps->read.buf, req); + return; + } + memcpy(cli_nps->read.buf, rcvbuf, received); + + if (cli_nps->trans.write_req == NULL) { + tstream_cli_np_readv_read_next(req); + return; + } + + tevent_schedule_immediate(state->trans.im, state->ev, + tstream_cli_np_readv_trans_next, req); + + tevent_req_done(cli_nps->trans.write_req); +} + +static void tstream_cli_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_cli_np_readv_read_next(req); +} + +static void tstream_cli_np_readv_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, struct tstream_cli_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, NT_STATUS_BUFFER_TOO_SMALL)) { + /* + * NT_STATUS_BUFFER_TOO_SMALL 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_cli_np_readv_disconnect_now(req, EIO, __location__); + return; + } + + if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) { + TALLOC_FREE(subreq); + tstream_cli_np_readv_disconnect_now(req, EIO, __location__); + return; + } + + if (received == 0) { + TALLOC_FREE(subreq); + tstream_cli_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_nomem(cli_nps->read.buf, req); + return; + } + memcpy(cli_nps->read.buf, rcvbuf, received); + TALLOC_FREE(subreq); + + tstream_cli_np_readv_read_next(req); +} + +static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq); + +static void tstream_cli_np_readv_error(struct tevent_req *req); + +static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req, + int error, + const char *location) +{ + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, + struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_cli_np_readv_error(req); + return; + } + + if (cli_nps->is_smb1) { + subreq = smb1cli_close_send(state, 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, 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 (subreq == NULL) { + /* return the original error */ + tstream_cli_np_readv_error(req); + return; + } + tevent_req_set_callback(subreq, + tstream_cli_np_readv_disconnect_done, + req); +} + +static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, struct tstream_cli_np); + + if (cli_nps->is_smb1) { + smb1cli_close_recv(subreq); + } else { + smb2cli_close_recv(subreq); + } + TALLOC_FREE(subreq); + + cli_nps->conn = NULL; + cli_nps->session = NULL; + cli_nps->tcon = NULL; + + tstream_cli_np_readv_error(req); +} + +static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); + +static void tstream_cli_np_readv_error(struct tevent_req *req) +{ + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, + struct tstream_cli_np_readv_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, + struct tstream_cli_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_cli_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_cli_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_cli_np_readv_state *state = + tevent_req_data(req, + struct tstream_cli_np_readv_state); + + /* return the original error */ + _tevent_req_error(req, state->error.val, state->error.location); +} + +static int tstream_cli_np_readv_recv(struct tevent_req *req, + int *perrno) +{ + struct tstream_cli_np_readv_state *state = + tevent_req_data(req, struct tstream_cli_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_cli_np_disconnect_state { + struct tstream_context *stream; +}; + +static void tstream_cli_np_disconnect_done(struct tevent_req *subreq); + +static struct tevent_req *tstream_cli_np_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream) +{ + struct tstream_cli_np *cli_nps = tstream_context_data(stream, + struct tstream_cli_np); + struct tevent_req *req; + struct tstream_cli_np_disconnect_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_cli_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_cli_np_disconnect_done, req); + + return req; +} + +static void tstream_cli_np_disconnect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_cli_np_disconnect_state *state = + tevent_req_data(req, struct tstream_cli_np_disconnect_state); + struct tstream_cli_np *cli_nps = + tstream_context_data(state->stream, struct tstream_cli_np); + NTSTATUS status; + + 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, EIO); + return; + } + + cli_nps->conn = NULL; + cli_nps->session = NULL; + cli_nps->tcon = NULL; + + tevent_req_done(req); +} + +static int tstream_cli_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_cli_np_ops = { + .name = "cli_np", + + .pending_bytes = tstream_cli_np_pending_bytes, + + .readv_send = tstream_cli_np_readv_send, + .readv_recv = tstream_cli_np_readv_recv, + + .writev_send = tstream_cli_np_writev_send, + .writev_recv = tstream_cli_np_writev_recv, + + .disconnect_send = tstream_cli_np_disconnect_send, + .disconnect_recv = tstream_cli_np_disconnect_recv, +}; diff --git a/libcli/smb/tstream_smbXcli_np.h b/libcli/smb/tstream_smbXcli_np.h new file mode 100644 index 0000000000..86d64a7230 --- /dev/null +++ b/libcli/smb/tstream_smbXcli_np.h @@ -0,0 +1,51 @@ +/* + 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/>. +*/ + +#ifndef _CLI_NP_TSTREAM_H_ +#define _CLI_NP_TSTREAM_H_ + +struct tevent_context; +struct tevent_req; +struct tstream_context; +struct smbXcli_conn; +struct smbXcli_session; +struct smbXcli_tcon; + +struct tevent_req *tstream_cli_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); +NTSTATUS _tstream_cli_np_open_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct tstream_context **_stream, + const char *location); +#define tstream_cli_np_open_recv(req, mem_ctx, stream) \ + _tstream_cli_np_open_recv(req, mem_ctx, stream, __location__) + +bool tstream_is_cli_np(struct tstream_context *stream); + +NTSTATUS tstream_cli_np_use_trans(struct tstream_context *stream); + +unsigned int tstream_cli_np_set_timeout(struct tstream_context *stream, + unsigned int timeout); + +#endif /* _CLI_NP_TSTREAM_H_ */ diff --git a/libcli/smb/wscript b/libcli/smb/wscript index 5239010047..f28b7454b4 100755 --- a/libcli/smb/wscript +++ b/libcli/smb/wscript @@ -38,6 +38,7 @@ def build(bld): smb2cli_query_directory.c smb2cli_ioctl.c smb2cli_echo.c + tstream_smbXcli_np.c ''', deps='LIBCRYPTO errors gensec krb5samba smb_transport', public_deps='talloc samba-util', @@ -49,5 +50,6 @@ def build(bld): smb2_lease.h smb_util.h smb_unix_ext.h + tstream_smbXcli_np.h ''', ) |