diff options
Diffstat (limited to 'source3/rpc_server/srv_pipe_hnd.c')
-rw-r--r-- | source3/rpc_server/srv_pipe_hnd.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c new file mode 100644 index 0000000..9ecbc57 --- /dev/null +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -0,0 +1,388 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-1998, + * Largely re-written : 2005 + * Copyright (C) Jeremy Allison 1998 - 2005 + * + * 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 "fake_file.h" +#include "rpc_dce.h" +#include "ntdomain.h" +#include "rpc_server/rpc_ncacn_np.h" +#include "rpc_server/srv_pipe_hnd.h" +#include "rpc_client/local_np.h" +#include "rpc_server/rpc_server.h" +#include "rpc_server/rpc_config.h" +#include "../lib/tsocket/tsocket.h" +#include "../lib/util/tevent_ntstatus.h" +#include "librpc/ndr/ndr_table.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +bool fsp_is_np(struct files_struct *fsp) +{ + enum FAKE_FILE_TYPE type; + + if ((fsp == NULL) || (fsp->fake_file_handle == NULL)) { + return false; + } + + type = fsp->fake_file_handle->type; + + return (type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY); +} + +NTSTATUS np_open(TALLOC_CTX *mem_ctx, const char *name, + const struct tsocket_address *remote_client_address, + const struct tsocket_address *local_server_address, + struct auth_session_info *session_info, + struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + struct dcesrv_context *dce_ctx, + struct fake_file_handle **phandle) +{ + struct fake_file_handle *handle; + struct npa_state *npa = NULL; + int ret; + + handle = talloc(mem_ctx, struct fake_file_handle); + if (handle == NULL) { + return NT_STATUS_NO_MEMORY; + } + + npa = npa_state_init(handle); + if (npa == NULL) { + TALLOC_FREE(handle); + return NT_STATUS_NO_MEMORY; + } + *handle = (struct fake_file_handle) { + .type = FAKE_FILE_TYPE_NAMED_PIPE_PROXY, + .private_data = npa, + }; + + ret = local_np_connect( + name, + NCACN_NP, + NULL, + remote_client_address, + NULL, + local_server_address, + session_info, + false, + npa, + &npa->stream); + if (ret != 0) { + DBG_DEBUG("local_np_connect failed: %s\n", + strerror(ret)); + TALLOC_FREE(handle); + return map_nt_error_from_unix(ret); + } + + *phandle = handle; + + return NT_STATUS_OK; +} + +bool np_read_in_progress(struct fake_file_handle *handle) +{ + if (handle->type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY) { + struct npa_state *p = + talloc_get_type_abort(handle->private_data, + struct npa_state); + size_t read_count; + + read_count = tevent_queue_length(p->read_queue); + if (read_count > 0) { + return true; + } + + return false; + } + + return false; +} + +struct np_write_state { + struct tevent_context *ev; + struct npa_state *p; + struct iovec iov; + ssize_t nwritten; +}; + +static void np_write_done(struct tevent_req *subreq); + +struct tevent_req *np_write_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct fake_file_handle *handle, + const uint8_t *data, size_t len) +{ + struct tevent_req *req; + struct np_write_state *state; + NTSTATUS status; + + DBG_INFO("len: %zu\n", len); + dump_data(50, data, len); + + req = tevent_req_create(mem_ctx, &state, struct np_write_state); + if (req == NULL) { + return NULL; + } + + if (len == 0) { + state->nwritten = 0; + status = NT_STATUS_OK; + goto post_status; + } + + if (handle->type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY) { + struct npa_state *p = talloc_get_type_abort( + handle->private_data, struct npa_state); + struct tevent_req *subreq; + + state->ev = ev; + state->p = p; + state->iov.iov_base = discard_const_p(void, data); + state->iov.iov_len = len; + + subreq = tstream_writev_queue_send(state, ev, + p->stream, + p->write_queue, + &state->iov, 1); + if (subreq == NULL) { + goto fail; + } + tevent_req_set_callback(subreq, np_write_done, req); + return req; + } + + status = NT_STATUS_INVALID_HANDLE; + post_status: + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + } else { + tevent_req_nterror(req, status); + } + return tevent_req_post(req, ev); + fail: + TALLOC_FREE(req); + return NULL; +} + +static void np_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct np_write_state *state = tevent_req_data( + req, struct np_write_state); + ssize_t received; + int err; + + received = tstream_writev_queue_recv(subreq, &err); + if (received < 0) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + state->nwritten = received; + tevent_req_done(req); +} + +NTSTATUS np_write_recv(struct tevent_req *req, ssize_t *pnwritten) +{ + struct np_write_state *state = tevent_req_data( + req, struct np_write_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pnwritten = state->nwritten; + return NT_STATUS_OK; +} + +struct np_ipc_readv_next_vector_state { + uint8_t *buf; + size_t len; + off_t ofs; + size_t remaining; +}; + +static void np_ipc_readv_next_vector_init(struct np_ipc_readv_next_vector_state *s, + uint8_t *buf, size_t len) +{ + ZERO_STRUCTP(s); + + s->buf = buf; + s->len = MIN(len, UINT16_MAX); +} + +static int np_ipc_readv_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *count) +{ + struct np_ipc_readv_next_vector_state *state = + (struct np_ipc_readv_next_vector_state *)private_data; + struct iovec *vector; + ssize_t pending; + size_t wanted; + + if (state->ofs == state->len) { + *_vector = NULL; + *count = 0; + return 0; + } + + pending = tstream_pending_bytes(stream); + if (pending == -1) { + return -1; + } + + if (pending == 0 && state->ofs != 0) { + /* return a short read */ + *_vector = NULL; + *count = 0; + return 0; + } + + if (pending == 0) { + /* we want at least one byte and recheck again */ + wanted = 1; + } else { + size_t missing = state->len - state->ofs; + if (pending > missing) { + /* there's more available */ + state->remaining = pending - missing; + wanted = missing; + } else { + /* read what we can get and recheck in the next cycle */ + wanted = pending; + } + } + + vector = talloc_array(mem_ctx, struct iovec, 1); + if (!vector) { + return -1; + } + + vector[0].iov_base = state->buf + state->ofs; + vector[0].iov_len = wanted; + + state->ofs += wanted; + + *_vector = vector; + *count = 1; + return 0; +} + +struct np_read_state { + struct npa_state *p; + struct np_ipc_readv_next_vector_state next_vector; + + ssize_t nread; + bool is_data_outstanding; +}; + +static void np_read_done(struct tevent_req *subreq); + +struct tevent_req *np_read_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct fake_file_handle *handle, + uint8_t *data, size_t len) +{ + struct tevent_req *req; + struct np_read_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, struct np_read_state); + if (req == NULL) { + return NULL; + } + + if (handle->type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY) { + struct npa_state *p = talloc_get_type_abort( + handle->private_data, struct npa_state); + struct tevent_req *subreq; + + np_ipc_readv_next_vector_init(&state->next_vector, + data, len); + + subreq = tstream_readv_pdu_queue_send(state, + ev, + p->stream, + p->read_queue, + np_ipc_readv_next_vector, + &state->next_vector); + if (subreq == NULL) { + status = NT_STATUS_NO_MEMORY; + goto post_status; + } + tevent_req_set_callback(subreq, np_read_done, req); + return req; + } + + status = NT_STATUS_INVALID_HANDLE; + post_status: + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + } else { + tevent_req_nterror(req, status); + } + return tevent_req_post(req, ev); +} + +static void np_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct np_read_state *state = tevent_req_data( + req, struct np_read_state); + ssize_t ret; + int err; + + ret = tstream_readv_pdu_queue_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + + state->nread = ret; + state->is_data_outstanding = (state->next_vector.remaining > 0); + + tevent_req_done(req); + return; +} + +NTSTATUS np_read_recv(struct tevent_req *req, ssize_t *nread, + bool *is_data_outstanding) +{ + struct np_read_state *state = tevent_req_data( + req, struct np_read_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + DEBUG(10, ("Received %d bytes. There is %smore data outstanding\n", + (int)state->nread, state->is_data_outstanding?"":"no ")); + + *nread = state->nread; + *is_data_outstanding = state->is_data_outstanding; + return NT_STATUS_OK; +} |