summaryrefslogtreecommitdiffstats
path: root/libcli/tstream_binding_handle/tstream_binding_handle.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libcli/tstream_binding_handle/tstream_binding_handle.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/libcli/tstream_binding_handle/tstream_binding_handle.c b/libcli/tstream_binding_handle/tstream_binding_handle.c
new file mode 100644
index 0000000..76f54a7
--- /dev/null
+++ b/libcli/tstream_binding_handle/tstream_binding_handle.c
@@ -0,0 +1,341 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Ralph Boehme 2016
+
+ 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 <tevent.h>
+#include "libcli/tstream_binding_handle/tstream_binding_handle.h"
+#include "system/filesys.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/debug.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+
+struct tstream_bh_state {
+ struct tstream_context *stream;
+ struct tevent_queue *write_queue;
+ const struct ndr_interface_table *table;
+ uint32_t request_timeout;
+ size_t call_initial_read_size;
+ tstream_read_pdu_blob_full_fn_t *complete_pdu_fn;
+ void *complete_pdu_fn_private;
+};
+
+static bool tstream_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct tstream_bh_state *hs = dcerpc_binding_handle_data(
+ h, struct tstream_bh_state);
+ ssize_t ret;
+
+ if (hs->stream == NULL) {
+ return false;
+ }
+
+ ret = tstream_pending_bytes(hs->stream);
+ if (ret == -1) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t tstream_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ struct tstream_bh_state *hs = dcerpc_binding_handle_data(
+ h, struct tstream_bh_state);
+ uint32_t old;
+
+ old = hs->request_timeout;
+ hs->request_timeout = timeout;
+
+ return old;
+}
+
+struct tstream_bh_disconnect_state {
+ struct tstream_bh_state *hs;
+};
+
+static void tstream_bh_disconnect_done(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_bh_disconnect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct tstream_bh_state *hs = dcerpc_binding_handle_data(
+ h, struct tstream_bh_state);
+ struct tevent_req *req = NULL;
+ struct tstream_bh_disconnect_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->hs = hs;
+
+ ok = tstream_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = tstream_disconnect_send(state, ev, hs->stream);
+ if (tevent_req_nomem(subreq, req)) {
+ tevent_req_post(req, ev);
+ return req;
+ }
+ tevent_req_set_callback(subreq, tstream_bh_disconnect_done, req);
+
+ return req;
+}
+
+static void tstream_bh_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct tstream_bh_disconnect_state *state = tevent_req_data(
+ req, struct tstream_bh_disconnect_state);
+ int ret, err;
+
+ ret = tstream_disconnect_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ DBG_ERR("tstream_bh_disconnect failed [%s]\n", strerror(err));
+ tevent_req_nterror(req, map_nt_error_from_unix_common(err));
+ return;
+ }
+
+ state->hs->stream = NULL;
+}
+
+static NTSTATUS tstream_bh_disconnect_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 tstream_bh_call_state {
+ struct tevent_context *ev;
+ struct tstream_context *stream;
+ struct tstream_bh_state *hs;
+ struct iovec out_data;
+ DATA_BLOB in_data;
+};
+
+static void tstream_bh_call_writev_done(struct tevent_req *subreq);
+static void tstream_bh_call_read_pdu_done(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_bh_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h,
+ const struct GUID *object,
+ uint32_t opnum,
+ uint32_t in_flags,
+ const uint8_t *out_data,
+ size_t out_length)
+{
+ struct tstream_bh_state *hs = dcerpc_binding_handle_data(
+ h, struct tstream_bh_state);
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct tstream_bh_call_state* state = NULL;
+ struct timeval timeout;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_bh_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ *state = (struct tstream_bh_call_state) {
+ .ev = ev,
+ .stream = hs->stream,
+ .hs = hs,
+ .out_data = {
+ .iov_base = discard_const_p(uint8_t, out_data),
+ .iov_len = out_length,
+ },
+ };
+
+ ok = tstream_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ if (tstream_is_smbXcli_np(hs->stream)) {
+ tstream_smbXcli_np_use_trans(hs->stream);
+ }
+ if (tevent_queue_length(hs->write_queue) > 0) {
+ tevent_req_nterror(req, NT_STATUS_PIPE_BUSY);
+ }
+
+ timeout = timeval_current_ofs(hs->request_timeout, 0);
+
+ subreq = tstream_writev_queue_send(state, ev,
+ state->stream,
+ hs->write_queue,
+ &state->out_data, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(subreq, ev, timeout)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, tstream_bh_call_writev_done, req);
+
+ subreq = tstream_read_pdu_blob_send(state,
+ ev,
+ hs->stream,
+ hs->call_initial_read_size,
+ hs->complete_pdu_fn,
+ hs->complete_pdu_fn_private);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(subreq, ev, timeout)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, tstream_bh_call_read_pdu_done, req);
+
+ return req;
+}
+
+static void tstream_bh_call_writev_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct tstream_bh_call_state *state = tevent_req_data(
+ req, struct tstream_bh_call_state);
+ int ret, err;
+
+ ret = tstream_writev_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ state->hs->stream = NULL;
+ tevent_req_nterror(req, map_nt_error_from_unix_common(err));
+ return;
+ }
+}
+
+static void tstream_bh_call_read_pdu_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct tstream_bh_call_state *state = tevent_req_data(
+ req, struct tstream_bh_call_state);
+ NTSTATUS status;
+
+ status = tstream_read_pdu_blob_recv(subreq, state, &state->in_data);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ state->hs->stream = NULL;
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS tstream_bh_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **in_data,
+ size_t *in_length,
+ uint32_t *in_flags)
+{
+ NTSTATUS status;
+ struct tstream_bh_call_state *state = tevent_req_data(
+ req, struct tstream_bh_call_state);
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *in_data = talloc_move(mem_ctx, &state->in_data.data);
+ *in_length = state->in_data.length;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static const struct dcerpc_binding_handle_ops tstream_bh_ops = {
+ .name = "tstream_binding_handle",
+ .is_connected = tstream_bh_is_connected,
+ .set_timeout = tstream_bh_set_timeout,
+ .raw_call_send = tstream_bh_call_send,
+ .raw_call_recv = tstream_bh_call_recv,
+ .disconnect_send = tstream_bh_disconnect_send,
+ .disconnect_recv = tstream_bh_disconnect_recv,
+};
+
+struct dcerpc_binding_handle *tstream_binding_handle_create(
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct tstream_context **stream,
+ size_t call_initial_read_size,
+ tstream_read_pdu_blob_full_fn_t *complete_pdu_fn,
+ void *complete_pdu_fn_private,
+ uint32_t max_data)
+{
+ struct dcerpc_binding_handle *h = NULL;
+ struct tstream_bh_state *hs = NULL;
+
+ h = dcerpc_binding_handle_create(mem_ctx,
+ &tstream_bh_ops,
+ NULL,
+ table,
+ &hs,
+ struct tstream_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ hs->table = table;
+ hs->stream = talloc_move(hs, stream);
+ hs->call_initial_read_size = call_initial_read_size;
+ hs->complete_pdu_fn = complete_pdu_fn;
+ hs->complete_pdu_fn_private = complete_pdu_fn_private;
+
+ hs->write_queue = tevent_queue_create(hs, "write_queue");
+ if (hs->write_queue == NULL) {
+ TALLOC_FREE(h);
+ return NULL;
+ }
+
+ if (max_data > 0) {
+ tstream_smbXcli_np_set_max_data(hs->stream, max_data);
+ }
+
+ return h;
+}