*/
#include "includes.h"
+#include "system/filesys.h"
#include "../lib/util/dlinklist.h"
#include "lib/events/events.h"
#include "librpc/rpc/dcerpc.h"
#include "librpc/rpc/dcerpc_proto.h"
#include "librpc/gen_ndr/ndr_misc.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
-#include "libcli/composite/composite.h"
#include "auth/gensec/gensec.h"
#include "param/param.h"
#include "lib/util/tevent_ntstatus.h"
#include "librpc/rpc/rpc_common.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+
enum rpc_request_state {
RPC_REQUEST_QUEUED,
uint16_t opnum;
DATA_BLOB request_data;
bool ignore_timeout;
-
- /* use by the ndr level async recv call */
- struct {
- const struct ndr_interface_table *table;
- uint32_t opnum;
- void *struct_ptr;
- TALLOC_CTX *mem_ctx;
- } ndr;
+ bool wait_for_sync;
struct {
void (*callback)(struct rpc_request *);
}
static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status);
-static void dcerpc_ship_next_request(struct dcecli_connection *c);
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c);
-static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
const struct GUID *object,
uint16_t opnum,
DATA_BLOB *stub_data);
ndr_push_flags_fn_t ndr_push,
ndr_pull_flags_fn_t ndr_pull,
ndr_print_function_t ndr_print);
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *p, NTSTATUS status);
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+ bool trigger_read);
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p);
/* destroy a dcerpc connection */
static int dcerpc_connection_destructor(struct dcecli_connection *conn)
c->security_state.generic_state = NULL;
c->binding_string = NULL;
c->flags = 0;
- c->srv_max_xmit_frag = 0;
- c->srv_max_recv_frag = 0;
+ /*
+ * Windows uses 5840 for ncacn_ip_tcp,
+ * so we also use it (for every transport)
+ * by default. But we give the transport
+ * the chance to overwrite it.
+ */
+ c->srv_max_xmit_frag = 5840;
+ c->srv_max_recv_frag = 5840;
c->pending = NULL;
+ c->io_trigger = tevent_create_immediate(c);
+ if (c->io_trigger == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+
talloc_set_destructor(c, dcerpc_connection_destructor);
return c;
return old;
}
+static void dcerpc_bh_auth_info(struct dcerpc_binding_handle *h,
+ enum dcerpc_AuthType *auth_type,
+ enum dcerpc_AuthLevel *auth_level)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p == NULL) {
+ return;
+ }
+
+ if (hs->p->conn == NULL) {
+ return;
+ }
+
+ if (hs->p->conn->security_state.auth_info == NULL) {
+ return;
+ }
+
+ *auth_type = hs->p->conn->security_state.auth_info->auth_type;
+ *auth_level = hs->p->conn->security_state.auth_info->auth_level;
+}
+
struct dcerpc_bh_raw_call_state {
+ struct tevent_context *ev;
struct dcerpc_binding_handle *h;
DATA_BLOB in_data;
DATA_BLOB out_data;
if (req == NULL) {
return NULL;
}
+ state->ev = ev;
state->h = h;
state->in_data.data = discard_const_p(uint8_t, in_data);
state->in_data.length = in_length;
return tevent_req_post(req, ev);
}
- subreq = dcerpc_request_send(hs->p,
+ subreq = dcerpc_request_send(state,
+ hs->p,
object,
opnum,
&state->in_data);
if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
status = dcerpc_fault_to_nt_status(fault_code);
}
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
return;
.name = "dcerpc",
.is_connected = dcerpc_bh_is_connected,
.set_timeout = dcerpc_bh_set_timeout,
+ .auth_info = dcerpc_bh_auth_info,
.raw_call_send = dcerpc_bh_raw_call_send,
.raw_call_recv = dcerpc_bh_raw_call_recv,
.disconnect_send = dcerpc_bh_disconnect_send,
struct ndr_pull *ndr;
enum ndr_err_code ndr_err;
- ndr = ndr_pull_init_flags(c, blob, mem_ctx);
+ ndr = ndr_pull_init_blob(blob, mem_ctx);
if (!ndr) {
return NT_STATUS_NO_MEMORY;
}
ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
}
+ if (CVAL(blob->data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ }
+
ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ TALLOC_FREE(ndr);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return ndr_map_error2ntstatus(ndr_err);
}
+ if (pkt->frag_length != blob->length) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
return NT_STATUS_OK;
}
/*
map a bind nak reason to a NTSTATUS
*/
-static NTSTATUS dcerpc_map_reason(uint16_t reason)
+static NTSTATUS dcerpc_map_nak_reason(enum dcerpc_bind_nak_reason reason)
{
switch (reason) {
- case DCERPC_BIND_REASON_ASYNTAX:
- return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
- case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
+ case DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED:
+ return NT_STATUS_REVISION_MISMATCH;
+ case DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE:
return NT_STATUS_INVALID_PARAMETER;
+ default:
+ break;
}
return NT_STATUS_UNSUCCESSFUL;
}
-/*
- a bind or alter context has failed
-*/
-static void dcerpc_composite_fail(struct rpc_request *req)
+static NTSTATUS dcerpc_map_ack_reason(const struct dcerpc_ack_ctx *ack)
{
- struct composite_context *c = talloc_get_type(req->async.private_data,
- struct composite_context);
- composite_error(c, req->status);
+ if (ack == NULL) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ switch (ack->result) {
+ case DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK:
+ /*
+ * We have not asked for this...
+ */
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ default:
+ break;
+ }
+
+ switch (ack->reason.value) {
+ case DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED:
+ return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+ case DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED:
+ return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+ default:
+ break;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
}
/*
conn->dead = true;
- if (conn->transport.shutdown_pipe) {
- conn->transport.shutdown_pipe(conn, status);
- }
+ TALLOC_FREE(conn->io_trigger);
+ conn->io_trigger_pending = false;
+
+ dcerpc_shutdown_pipe(conn, status);
/* all pending requests get the error */
while (conn->pending) {
}
}
+ /* all requests, which are not shipped */
+ while (conn->request_queue) {
+ struct rpc_request *req = conn->request_queue;
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = status;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ }
+
talloc_set_destructor(conn, NULL);
if (conn->free_skipped) {
talloc_free(conn);
{
struct ncacn_packet pkt;
+ if (conn->dead) {
+ return;
+ }
+
if (NT_STATUS_IS_OK(status) && blob->length == 0) {
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
}
if (!NT_STATUS_IS_OK(status)) {
data_blob_free(blob);
dcerpc_connection_dead(conn, status);
- }
-
- dcerpc_request_recv_data(conn, blob, &pkt);
-}
-
-/*
- Receive a bind reply from the transport
-*/
-static void dcerpc_bind_recv_handler(struct rpc_request *req,
- DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
-{
- struct composite_context *c;
- struct dcecli_connection *conn;
-
- c = talloc_get_type(req->async.private_data, struct composite_context);
-
- if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
- DEBUG(2,("dcerpc: bind_nak reason %d\n",
- pkt->u.bind_nak.reject_reason));
- composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
- reject_reason));
- return;
- }
-
- if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
- (pkt->u.bind_ack.num_results == 0) ||
- (pkt->u.bind_ack.ctx_list[0].result != 0)) {
- req->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
- composite_error(c, NT_STATUS_NET_WRITE_FAULT);
return;
}
- conn = req->p->conn;
-
- conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
- conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
-
- if ((req->p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) &&
- (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
- conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
- }
-
- if ((req->p->binding->flags & DCERPC_HEADER_SIGNING) &&
- (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
- conn->flags |= DCERPC_HEADER_SIGNING;
- }
-
- /* the bind_ack might contain a reply set of credentials */
- if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
- NTSTATUS status;
- uint32_t auth_length;
- status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
- conn->security_state.auth_info, &auth_length, true);
- if (!NT_STATUS_IS_OK(status)) {
- composite_error(c, status);
- return;
- }
- }
-
- req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
-
- composite_done(c);
+ dcerpc_request_recv_data(conn, blob, &pkt);
}
/*
dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
}
-/*
- send a async dcerpc bind request
-*/
-struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
- TALLOC_CTX *mem_ctx,
- const struct ndr_syntax_id *syntax,
- const struct ndr_syntax_id *transfer_syntax)
+struct dcerpc_bind_state {
+ struct tevent_context *ev;
+ struct dcerpc_pipe *p;
+};
+
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq);
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_bind_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_pipe *p,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
{
- struct composite_context *c;
+ struct tevent_req *req;
+ struct dcerpc_bind_state *state;
struct ncacn_packet pkt;
DATA_BLOB blob;
- struct rpc_request *req;
+ NTSTATUS status;
+ struct rpc_request *subreq;
+ uint32_t flags;
- c = composite_create(mem_ctx,p->conn->event_ctx);
- if (c == NULL) return NULL;
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_bind_state);
+ if (req == NULL) {
+ return NULL;
+ }
- c->private_data = p;
+ state->ev = ev;
+ state->p = p;
p->syntax = *syntax;
p->transfer_syntax = *transfer_syntax;
+ flags = dcerpc_binding_get_flags(p->binding);
+
init_ncacn_hdr(p->conn, &pkt);
pkt.ptype = DCERPC_PKT_BIND;
pkt.call_id = p->conn->call_id;
pkt.auth_length = 0;
- if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
}
- if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+ if (p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
}
- pkt.u.bind.max_xmit_frag = 5840;
- pkt.u.bind.max_recv_frag = 5840;
- pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
+ pkt.u.bind.max_xmit_frag = p->conn->srv_max_xmit_frag;
+ pkt.u.bind.max_recv_frag = p->conn->srv_max_recv_frag;
+ pkt.u.bind.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
pkt.u.bind.num_contexts = 1;
pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
- if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
+ if (tevent_req_nomem(pkt.u.bind.ctx_list, req)) {
+ return tevent_req_post(req, ev);
+ }
pkt.u.bind.ctx_list[0].context_id = p->context_id;
pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
pkt.u.bind.auth_info = data_blob(NULL, 0);
/* construct the NDR form of the packet */
- c->status = ncacn_push_auth(&blob, c, &pkt,
- p->conn->security_state.auth_info);
- if (!composite_is_ok(c)) return c;
-
- p->conn->transport.recv_data = dcerpc_recv_data;
+ status = ncacn_push_auth(&blob, state, &pkt,
+ p->conn->security_state.auth_info);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
/*
* we allocate a dcerpc_request so we can be in the same
* request queue as normal requests
*/
- req = talloc_zero(c, struct rpc_request);
- if (composite_nomem(req, c)) return c;
+ subreq = talloc_zero(state, struct rpc_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq->state = RPC_REQUEST_PENDING;
+ subreq->call_id = pkt.call_id;
+ subreq->async.private_data = req;
+ subreq->async.callback = dcerpc_bind_fail_handler;
+ subreq->p = p;
+ subreq->recv_handler = dcerpc_bind_recv_handler;
+ DLIST_ADD_END(p->conn->pending, subreq, struct rpc_request *);
+ talloc_set_destructor(subreq, dcerpc_req_dequeue);
+
+ status = dcerpc_send_request(p->conn, &blob, true);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_add_timer(ev, subreq,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, subreq);
+
+ return req;
+}
+
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_bind_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bind_state);
+ NTSTATUS status = subreq->status;
+
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
- req->state = RPC_REQUEST_PENDING;
- req->call_id = pkt.call_id;
- req->async.private_data = c;
- req->async.callback = dcerpc_composite_fail;
- req->p = p;
- req->recv_handler = dcerpc_bind_recv_handler;
- DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
- talloc_set_destructor(req, dcerpc_req_dequeue);
+ tevent_req_nterror(req, status);
+}
+
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_bind_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bind_state);
+ struct dcecli_connection *conn = state->p->conn;
+ NTSTATUS status;
+ uint32_t flags;
- c->status = p->conn->transport.send_request(p->conn, &blob,
- true);
- if (!composite_is_ok(c)) return c;
+ /*
+ * Note that pkt is allocated under raw_packet->data,
+ * while raw_packet->data is a child of subreq.
+ */
+ talloc_steal(state, raw_packet->data);
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
+ status = dcerpc_map_nak_reason(pkt->u.bind_nak.reject_reason);
- tevent_add_timer(c->event_ctx, req,
- timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
- dcerpc_timeout_handler, req);
+ DEBUG(2,("dcerpc: bind_nak reason %d - %s\n",
+ pkt->u.bind_nak.reject_reason, nt_errstr(status)));
- return c;
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
+ (pkt->u.bind_ack.num_results == 0) ||
+ (pkt->u.bind_ack.ctx_list[0].result != 0)) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ /*
+ * DCE-RPC 1.1 (c706) specifies
+ * CONST_MUST_RCV_FRAG_SIZE as 1432
+ */
+ if (pkt->u.bind_ack.max_xmit_frag < 1432) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+ if (pkt->u.bind_ack.max_recv_frag < 1432) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+ conn->srv_max_xmit_frag = MIN(conn->srv_max_xmit_frag,
+ pkt->u.bind_ack.max_xmit_frag);
+ conn->srv_max_recv_frag = MIN(conn->srv_max_recv_frag,
+ pkt->u.bind_ack.max_recv_frag);
+
+ flags = dcerpc_binding_get_flags(state->p->binding);
+
+ if ((flags & DCERPC_CONCURRENT_MULTIPLEX) &&
+ (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
+ conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+ }
+
+ if ((conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) &&
+ (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
+ conn->flags |= DCERPC_HEADER_SIGNING;
+ }
+
+ /* the bind_ack might contain a reply set of credentials */
+ if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
+ uint32_t auth_length;
+
+ status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
+ conn->security_state.auth_info, &auth_length, true);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+
+ status = dcerpc_binding_set_assoc_group_id(state->p->binding,
+ pkt->u.bind_ack.assoc_group_id);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
}
-/*
- recv side of async dcerpc bind request
-*/
-NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
+NTSTATUS dcerpc_bind_recv(struct tevent_req *req)
{
- NTSTATUS result = composite_wait(ctx);
- talloc_free(ctx);
- return result;
+ return tevent_req_simple_recv_ntstatus(req);
}
/*
struct ncacn_packet pkt;
NTSTATUS status;
DATA_BLOB blob;
+ uint32_t flags;
+
+ flags = dcerpc_binding_get_flags(p->binding);
init_ncacn_hdr(p->conn, &pkt);
pkt.auth_length = 0;
pkt.u.auth3.auth_info = data_blob(NULL, 0);
- if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
}
- if (p->binding->flags & DCERPC_HEADER_SIGNING) {
- pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
- }
-
/* construct the NDR form of the packet */
status = ncacn_push_auth(&blob, mem_ctx,
&pkt,
}
/* send it on its way */
- status = p->conn->transport.send_request(p->conn, &blob, false);
+ status = dcerpc_send_request(p->conn, &blob, false);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (req->recv_handler != NULL) {
dcerpc_req_dequeue(req);
req->state = RPC_REQUEST_DONE;
+
+ /*
+ * We have to look at shipping further requests before calling
+ * the async function, that one might close the pipe
+ */
+ dcerpc_schedule_io_trigger(c);
+
req->recv_handler(req, raw_packet, pkt);
return;
}
}
if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
- c->transport.send_read(c);
+ data_blob_free(raw_packet);
+ dcerpc_send_read(c);
return;
}
req->flags &= ~DCERPC_PULL_BIGENDIAN;
}
-
req_done:
+ data_blob_free(raw_packet);
+
/* we've got the full payload */
+ dcerpc_req_dequeue(req);
req->state = RPC_REQUEST_DONE;
- DLIST_REMOVE(c->pending, req);
- if (c->request_queue != NULL) {
- /* We have to look at shipping further requests before calling
- * the async function, that one might close the pipe */
- dcerpc_ship_next_request(c);
- }
+ /*
+ * We have to look at shipping further requests before calling
+ * the async function, that one might close the pipe
+ */
+ dcerpc_schedule_io_trigger(c);
if (req->async.callback) {
req->async.callback(req);
/*
perform the send side of a async dcerpc request
*/
-static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
const struct GUID *object,
uint16_t opnum,
DATA_BLOB *stub_data)
{
struct rpc_request *req;
- p->conn->transport.recv_data = dcerpc_recv_data;
-
- req = talloc(p, struct rpc_request);
+ req = talloc_zero(mem_ctx, struct rpc_request);
if (req == NULL) {
return NULL;
}
req->p = p;
req->call_id = next_call_id(p->conn);
- req->status = NT_STATUS_OK;
req->state = RPC_REQUEST_QUEUED;
- req->payload = data_blob(NULL, 0);
- req->flags = 0;
- req->fault_code = 0;
- req->ignore_timeout = false;
- req->async.callback = NULL;
- req->async.private_data = NULL;
- req->recv_handler = NULL;
if (object != NULL) {
req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
talloc_free(req);
return NULL;
}
- } else {
- req->object = NULL;
}
req->opnum = opnum;
req->request_data.length = stub_data->length;
- req->request_data.data = talloc_reference(req, stub_data->data);
- if (req->request_data.length && req->request_data.data == NULL) {
- return NULL;
- }
+ req->request_data.data = stub_data->data;
DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
talloc_set_destructor(req, dcerpc_req_dequeue);
- dcerpc_ship_next_request(p->conn);
+ dcerpc_schedule_io_trigger(p->conn);
if (p->request_timeout) {
- tevent_add_timer(dcerpc_event_context(p), req,
+ tevent_add_timer(p->conn->event_ctx, req,
timeval_current_ofs(p->request_timeout, 0),
dcerpc_timeout_handler, req);
}
bool first_packet = true;
size_t sig_size = 0;
bool need_async = false;
+ bool can_async = true;
req = c->request_queue;
if (req == NULL) {
need_async = true;
}
+ if (c->security_state.auth_info &&
+ c->security_state.generic_state)
+ {
+ struct gensec_security *gensec = c->security_state.generic_state;
+
+ switch (c->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ can_async = gensec_have_feature(gensec,
+ GENSEC_FEATURE_ASYNC_REPLIES);
+ break;
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ case DCERPC_AUTH_LEVEL_NONE:
+ can_async = true;
+ break;
+ default:
+ can_async = false;
+ break;
+ }
+ }
+
+ if (need_async && !can_async) {
+ req->wait_for_sync = true;
+ return;
+ }
+
DLIST_REMOVE(c->request_queue, req);
DLIST_ADD(c->pending, req);
req->state = RPC_REQUEST_PENDING;
pkt.call_id = req->call_id;
pkt.auth_length = 0;
pkt.pfc_flags = 0;
- pkt.u.request.alloc_hint = remaining;
pkt.u.request.context_id = p->context_id;
pkt.u.request.opnum = req->opnum;
last_frag = true;
}
+ pkt.u.request.alloc_hint = remaining;
pkt.u.request.stub_and_verifier.data = stub_data->data +
(stub_data->length - remaining);
pkt.u.request.stub_and_verifier.length = chunk;
do_trans = true;
}
- req->status = p->conn->transport.send_request(p->conn, &blob, do_trans);
+ req->status = dcerpc_send_request(p->conn, &blob, do_trans);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
DLIST_REMOVE(p->conn->pending, req);
}
if (last_frag && !do_trans) {
- req->status = p->conn->transport.send_read(p->conn);
+ req->status = dcerpc_send_read(p->conn);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
DLIST_REMOVE(p->conn->pending, req);
}
}
-/*
- return the event context for a dcerpc pipe
- used by callers who wish to operate asynchronously
-*/
-_PUBLIC_ struct tevent_context *dcerpc_event_context(struct dcerpc_pipe *p)
+static void dcerpc_io_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
{
- return p->conn->event_ctx;
-}
+ struct dcecli_connection *c =
+ talloc_get_type_abort(private_data,
+ struct dcecli_connection);
+ c->io_trigger_pending = false;
+ dcerpc_schedule_io_trigger(c);
-/*
- perform the receive side of a async dcerpc request
-*/
-static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *stub_data)
-{
- NTSTATUS status;
+ dcerpc_ship_next_request(c);
+}
- while (req->state != RPC_REQUEST_DONE) {
- struct tevent_context *ctx = dcerpc_event_context(req->p);
- if (tevent_loop_once(ctx) != 0) {
- return NT_STATUS_CONNECTION_DISCONNECTED;
- }
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c)
+{
+ if (c->dead) {
+ return;
+ }
+
+ if (c->request_queue == NULL) {
+ return;
+ }
+
+ if (c->request_queue->wait_for_sync && c->pending) {
+ return;
+ }
+
+ if (c->io_trigger_pending) {
+ return;
+ }
+
+ c->io_trigger_pending = true;
+
+ tevent_schedule_immediate(c->io_trigger,
+ c->event_ctx,
+ dcerpc_io_trigger,
+ c);
+}
+
+/*
+ perform the receive side of a async dcerpc request
+*/
+static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data)
+{
+ NTSTATUS status;
+
+ while (req->state != RPC_REQUEST_DONE) {
+ struct tevent_context *ctx = req->p->conn->event_ctx;
+ if (tevent_loop_once(ctx) != 0) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
}
*stub_data = req->payload;
status = req->status;
*/
_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
{
- if (!p->conn->transport.target_hostname) {
- if (!p->conn->transport.peer_name) {
- return "";
- }
- return p->conn->transport.peer_name(p->conn);
- }
- return p->conn->transport.target_hostname(p->conn);
+ return p->conn ? p->conn->server_name : NULL;
}
return auth_level;
}
-/*
- Receive an alter reply from the transport
-*/
-static void dcerpc_alter_recv_handler(struct rpc_request *req,
- DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+struct dcerpc_alter_context_state {
+ struct tevent_context *ev;
+ struct dcerpc_pipe *p;
+};
+
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq);
+static void dcerpc_alter_context_recv_handler(struct rpc_request *req,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_alter_context_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_pipe *p,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
{
- struct composite_context *c;
- struct dcerpc_pipe *recv_pipe;
+ struct tevent_req *req;
+ struct dcerpc_alter_context_state *state;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct rpc_request *subreq;
+ uint32_t flags;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_alter_context_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->p = p;
- c = talloc_get_type(req->async.private_data, struct composite_context);
- recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
+ p->syntax = *syntax;
+ p->transfer_syntax = *transfer_syntax;
+
+ flags = dcerpc_binding_get_flags(p->binding);
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_ALTER;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->conn->call_id;
+ pkt.auth_length = 0;
+
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ pkt.u.alter.max_xmit_frag = p->conn->srv_max_xmit_frag;
+ pkt.u.alter.max_recv_frag = p->conn->srv_max_recv_frag;
+ pkt.u.alter.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
+ pkt.u.alter.num_contexts = 1;
+ pkt.u.alter.ctx_list = talloc_array(state, struct dcerpc_ctx_list, 1);
+ if (tevent_req_nomem(pkt.u.alter.ctx_list, req)) {
+ return tevent_req_post(req, ev);
+ }
+ pkt.u.alter.ctx_list[0].context_id = p->context_id;
+ pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
+ pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+ pkt.u.alter.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ status = ncacn_push_auth(&blob, state, &pkt,
+ p->conn->security_state.auth_info);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we allocate a dcerpc_request so we can be in the same
+ * request queue as normal requests
+ */
+ subreq = talloc_zero(state, struct rpc_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq->state = RPC_REQUEST_PENDING;
+ subreq->call_id = pkt.call_id;
+ subreq->async.private_data = req;
+ subreq->async.callback = dcerpc_alter_context_fail_handler;
+ subreq->p = p;
+ subreq->recv_handler = dcerpc_alter_context_recv_handler;
+ DLIST_ADD_END(p->conn->pending, subreq, struct rpc_request *);
+ talloc_set_destructor(subreq, dcerpc_req_dequeue);
+
+ status = dcerpc_send_request(p->conn, &blob, true);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_add_timer(ev, subreq,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, subreq);
+
+ return req;
+}
+
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_alter_context_state *state =
+ tevent_req_data(req,
+ struct dcerpc_alter_context_state);
+ NTSTATUS status = subreq->status;
+
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ tevent_req_nterror(req, status);
+}
+
+static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_alter_context_state *state =
+ tevent_req_data(req,
+ struct dcerpc_alter_context_state);
+ struct dcecli_connection *conn = state->p->conn;
+ NTSTATUS status;
+
+ /*
+ * Note that pkt is allocated under raw_packet->data,
+ * while raw_packet->data is a child of subreq.
+ */
+ talloc_steal(state, raw_packet->data);
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
pkt->u.alter_resp.num_results == 1 &&
pkt->u.alter_resp.ctx_list[0].result != 0) {
- DEBUG(2,("dcerpc: alter_resp failed - reason %d\n",
- pkt->u.alter_resp.ctx_list[0].reason));
- composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
+ status = dcerpc_map_ack_reason(&pkt->u.alter_resp.ctx_list[0]);
+ DEBUG(2,("dcerpc: alter_resp failed - reason %d - %s\n",
+ pkt->u.alter_resp.ctx_list[0].reason.value,
+ nt_errstr(status)));
+ tevent_req_nterror(req, status);
return;
}
if (pkt->ptype == DCERPC_PKT_FAULT) {
- DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
- recv_pipe->last_fault_code = pkt->u.fault.status;
- composite_error(c, NT_STATUS_NET_WRITE_FAULT);
+ DEBUG(5,("dcerpc: alter_resp - rpc fault: %s\n",
+ dcerpc_errstr(state, pkt->u.fault.status)));
+ if (pkt->u.fault.status == DCERPC_FAULT_ACCESS_DENIED) {
+ state->p->last_fault_code = pkt->u.fault.status;
+ tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+ } else {
+ state->p->last_fault_code = pkt->u.fault.status;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ }
return;
}
if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
pkt->u.alter_resp.num_results == 0 ||
pkt->u.alter_resp.ctx_list[0].result != 0) {
- recv_pipe->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
- composite_error(c, NT_STATUS_NET_WRITE_FAULT);
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
return;
}
/* the alter_resp might contain a reply set of credentials */
- if (recv_pipe->conn->security_state.auth_info &&
+ if (conn->security_state.auth_info &&
pkt->u.alter_resp.auth_info.length) {
- struct dcecli_connection *conn = recv_pipe->conn;
- NTSTATUS status;
uint32_t auth_length;
+
status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.alter_resp.auth_info,
conn->security_state.auth_info, &auth_length, true);
- if (!NT_STATUS_IS_OK(status)) {
- composite_error(c, status);
+ if (tevent_req_nterror(req, status)) {
return;
}
}
- composite_done(c);
+ tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_alter_context_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
}
/*
send a dcerpc alter_context request
*/
-struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
- TALLOC_CTX *mem_ctx,
- const struct ndr_syntax_id *syntax,
- const struct ndr_syntax_id *transfer_syntax)
+_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
{
- struct composite_context *c;
- struct ncacn_packet pkt;
- DATA_BLOB blob;
- struct rpc_request *req;
+ struct tevent_req *subreq;
+ struct tevent_context *ev = p->conn->event_ctx;
+ bool ok;
- c = composite_create(mem_ctx, p->conn->event_ctx);
- if (c == NULL) return NULL;
+ /* TODO: create a new event context here */
- c->private_data = p;
+ subreq = dcerpc_alter_context_send(mem_ctx, ev,
+ p, syntax, transfer_syntax);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
- p->syntax = *syntax;
- p->transfer_syntax = *transfer_syntax;
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ NTSTATUS status;
+ status = map_nt_error_from_unix_common(errno);
+ return status;
+ }
- init_ncacn_hdr(p->conn, &pkt);
+ return dcerpc_alter_context_recv(subreq);
+}
- pkt.ptype = DCERPC_PKT_ALTER;
- pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
- pkt.call_id = p->conn->call_id;
- pkt.auth_length = 0;
+static void dcerpc_transport_dead(struct dcecli_connection *c, NTSTATUS status)
+{
+ if (c->transport.stream == NULL) {
+ return;
+ }
- if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
- pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ tevent_queue_stop(c->transport.write_queue);
+ TALLOC_FREE(c->transport.read_subreq);
+ TALLOC_FREE(c->transport.stream);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
}
- if (p->binding->flags & DCERPC_HEADER_SIGNING) {
- pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+ status = NT_STATUS_END_OF_FILE;
}
- pkt.u.alter.max_xmit_frag = 5840;
- pkt.u.alter.max_recv_frag = 5840;
- pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
- pkt.u.alter.num_contexts = 1;
- pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
- if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
- pkt.u.alter.ctx_list[0].context_id = p->context_id;
- pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
- pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
- pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
- pkt.u.alter.auth_info = data_blob(NULL, 0);
+ dcerpc_recv_data(c, NULL, status);
+}
- /* construct the NDR form of the packet */
- c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
- p->conn->security_state.auth_info);
- if (!composite_is_ok(c)) return c;
- p->conn->transport.recv_data = dcerpc_recv_data;
+/*
+ shutdown SMB pipe connection
+*/
+struct dcerpc_shutdown_pipe_state {
+ struct dcecli_connection *c;
+ NTSTATUS status;
+};
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
+{
+ struct dcerpc_shutdown_pipe_state *state;
+ struct tevent_req *subreq;
+
+ if (c->transport.stream == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ state = talloc_zero(c, struct dcerpc_shutdown_pipe_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->c = c;
+ state->status = status;
+
+ subreq = tstream_disconnect_send(state, c->event_ctx, c->transport.stream);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, dcerpc_shutdown_pipe_done, state);
+
+ return status;
+}
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq)
+{
+ struct dcerpc_shutdown_pipe_state *state =
+ tevent_req_callback_data(subreq, struct dcerpc_shutdown_pipe_state);
+ struct dcecli_connection *c = state->c;
+ NTSTATUS status = state->status;
+ int error;
/*
- * we allocate a dcerpc_request so we can be in the same
- * request queue as normal requests
+ * here we ignore the return values...
*/
- req = talloc_zero(c, struct rpc_request);
- if (composite_nomem(req, c)) return c;
+ tstream_disconnect_recv(subreq, &error);
+ TALLOC_FREE(subreq);
- req->state = RPC_REQUEST_PENDING;
- req->call_id = pkt.call_id;
- req->async.private_data = c;
- req->async.callback = dcerpc_composite_fail;
- req->p = p;
- req->recv_handler = dcerpc_alter_recv_handler;
- DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
- talloc_set_destructor(req, dcerpc_req_dequeue);
+ TALLOC_FREE(state);
- c->status = p->conn->transport.send_request(p->conn, &blob, true);
- if (!composite_is_ok(c)) return c;
+ dcerpc_transport_dead(c, status);
+}
- tevent_add_timer(c->event_ctx, req,
- timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
- dcerpc_timeout_handler, req);
- return c;
+
+struct dcerpc_send_read_state {
+ struct dcecli_connection *p;
+};
+
+static int dcerpc_send_read_state_destructor(struct dcerpc_send_read_state *state)
+{
+ struct dcecli_connection *p = state->p;
+
+ p->transport.read_subreq = NULL;
+
+ return 0;
}
-NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
+static void dcerpc_send_read_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p)
{
- NTSTATUS result = composite_wait(ctx);
- talloc_free(ctx);
- return result;
+ struct dcerpc_send_read_state *state;
+
+ if (p->transport.read_subreq != NULL) {
+ p->transport.pending_reads++;
+ return NT_STATUS_OK;
+ }
+
+ state = talloc_zero(p, struct dcerpc_send_read_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->p = p;
+
+ talloc_set_destructor(state, dcerpc_send_read_state_destructor);
+
+ p->transport.read_subreq = dcerpc_read_ncacn_packet_send(state,
+ p->event_ctx,
+ p->transport.stream);
+ if (p->transport.read_subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(p->transport.read_subreq, dcerpc_send_read_done, state);
+
+ return NT_STATUS_OK;
}
-/*
- send a dcerpc alter_context request
-*/
-_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
- TALLOC_CTX *mem_ctx,
- const struct ndr_syntax_id *syntax,
- const struct ndr_syntax_id *transfer_syntax)
+static void dcerpc_send_read_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_read_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_read_state);
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status;
+ struct ncacn_packet *pkt;
+ DATA_BLOB blob;
+
+ status = dcerpc_read_ncacn_packet_recv(subreq, state,
+ &pkt, &blob);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+
+ /*
+ * here we steal into thet connection context,
+ * but p->transport.recv_data() will steal or free it again
+ */
+ talloc_steal(p, blob.data);
+ TALLOC_FREE(state);
+
+ if (p->transport.pending_reads > 0) {
+ p->transport.pending_reads--;
+
+ status = dcerpc_send_read(p);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+ }
+
+ dcerpc_recv_data(p, &blob, NT_STATUS_OK);
+}
+
+struct dcerpc_send_request_state {
+ struct dcecli_connection *p;
+ DATA_BLOB blob;
+ struct iovec iov;
+};
+
+static int dcerpc_send_request_state_destructor(struct dcerpc_send_request_state *state)
{
- struct composite_context *creq;
- creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
- return dcerpc_alter_context_recv(creq);
+ struct dcecli_connection *p = state->p;
+
+ p->transport.read_subreq = NULL;
+
+ return 0;
}
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq);
+static void dcerpc_send_request_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+ bool trigger_read)
+{
+ struct dcerpc_send_request_state *state;
+ struct tevent_req *subreq;
+ bool use_trans = trigger_read;
+
+ if (p->transport.stream == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ state = talloc_zero(p, struct dcerpc_send_request_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->p = p;
+
+ state->blob = data_blob_talloc(state, data->data, data->length);
+ if (state->blob.data == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->iov.iov_base = (void *)state->blob.data;
+ state->iov.iov_len = state->blob.length;
+
+ if (p->transport.read_subreq != NULL) {
+ use_trans = false;
+ }
+
+ if (!tstream_is_smbXcli_np(p->transport.stream)) {
+ use_trans = false;
+ }
+
+ if (use_trans) {
+ /*
+ * we need to block reads until our write is
+ * the next in the write queue.
+ */
+ p->transport.read_subreq = tevent_queue_wait_send(state, p->event_ctx,
+ p->transport.write_queue);
+ if (p->transport.read_subreq == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(p->transport.read_subreq,
+ dcerpc_send_request_wait_done,
+ state);
+
+ talloc_set_destructor(state, dcerpc_send_request_state_destructor);
+
+ trigger_read = false;
+ }
+
+ subreq = tstream_writev_queue_send(state, p->event_ctx,
+ p->transport.stream,
+ p->transport.write_queue,
+ &state->iov, 1);
+ if (subreq == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, dcerpc_send_request_done, state);
+
+ if (trigger_read) {
+ dcerpc_send_read(p);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_request_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_request_state);
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status;
+ bool ok;
+
+ p->transport.read_subreq = NULL;
+ talloc_set_destructor(state, NULL);
+
+ ok = tevent_queue_wait_recv(subreq);
+ if (!ok) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ if (tevent_queue_length(p->transport.write_queue) <= 2) {
+ status = tstream_smbXcli_np_use_trans(p->transport.stream);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+ }
+
+ /* we free subreq after tstream_cli_np_use_trans */
+ TALLOC_FREE(subreq);
+
+ dcerpc_send_read(p);
+}
+
+static void dcerpc_send_request_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_request_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_request_state);
+ int ret;
+ int error;
+
+ ret = tstream_writev_queue_recv(subreq, &error);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status = map_nt_error_from_unix_common(error);
+
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+
+ TALLOC_FREE(state);
+}