diff options
Diffstat (limited to 'libcli/tstream_binding_handle')
-rw-r--r-- | libcli/tstream_binding_handle/tstream_binding_handle.c | 341 | ||||
-rw-r--r-- | libcli/tstream_binding_handle/tstream_binding_handle.h | 37 | ||||
-rw-r--r-- | libcli/tstream_binding_handle/wscript_build | 5 |
3 files changed, 383 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; +} diff --git a/libcli/tstream_binding_handle/tstream_binding_handle.h b/libcli/tstream_binding_handle/tstream_binding_handle.h new file mode 100644 index 0000000..e540737 --- /dev/null +++ b/libcli/tstream_binding_handle/tstream_binding_handle.h @@ -0,0 +1,37 @@ +/* + 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/>. +*/ + +#ifndef _TSTREAM_BINDING_HANDLE_H_ +#define _TSTREAM_BINDING_HANDLE_H_ + +#include <talloc.h> +#include "librpc/rpc/dcerpc.h" +#include "librpc/rpc/rpc_common.h" +#include "libcli/util/tstream.h" + +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); + +#endif /* _TSTREAM_BINDING_HANDLE_H_ */ diff --git a/libcli/tstream_binding_handle/wscript_build b/libcli/tstream_binding_handle/wscript_build new file mode 100644 index 0000000..622ed70 --- /dev/null +++ b/libcli/tstream_binding_handle/wscript_build @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +bld.SAMBA_SUBSYSTEM('tstream_binding_handle', + source='tstream_binding_handle.c', + deps='tevent LIBTSOCKET') |