+
+struct irpc_bh_state {
+ struct imessaging_context *msg_ctx;
+ struct server_id server_id;
+ const struct ndr_interface_table *table;
+ uint32_t timeout;
+ struct security_token *token;
+};
+
+static bool irpc_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+
+ if (!hs->msg_ctx) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t irpc_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ uint32_t old = hs->timeout;
+
+ hs->timeout = timeout;
+
+ return old;
+}
+
+struct irpc_bh_raw_call_state {
+ struct irpc_request *irpc;
+ uint32_t opnum;
+ DATA_BLOB in_data;
+ DATA_BLOB in_packet;
+ DATA_BLOB out_data;
+};
+
+static void irpc_bh_raw_call_incoming_handler(struct irpc_request *irpc,
+ struct irpc_message *m);
+
+static struct tevent_req *irpc_bh_raw_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 *in_data,
+ size_t in_length)
+{
+ struct irpc_bh_state *hs =
+ dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ struct tevent_req *req;
+ struct irpc_bh_raw_call_state *state;
+ bool ok;
+ struct irpc_header header;
+ struct ndr_push *ndr;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct irpc_bh_raw_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->opnum = opnum;
+ state->in_data.data = discard_const_p(uint8_t, in_data);
+ state->in_data.length = in_length;
+
+ ok = irpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ state->irpc = talloc_zero(state, struct irpc_request);
+ if (tevent_req_nomem(state->irpc, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->irpc->msg_ctx = hs->msg_ctx;
+ state->irpc->callid = idr_get_new(hs->msg_ctx->idr,
+ state->irpc, UINT16_MAX);
+ if (state->irpc->callid == -1) {
+ tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+ state->irpc->incoming.handler = irpc_bh_raw_call_incoming_handler;
+ state->irpc->incoming.private_data = req;
+
+ talloc_set_destructor(state->irpc, irpc_destructor);
+
+ /* setup the header */
+ header.uuid = hs->table->syntax_id.uuid;
+
+ header.if_version = hs->table->syntax_id.if_version;
+ header.callid = state->irpc->callid;
+ header.callnum = state->opnum;
+ header.flags = 0;
+ header.status = NT_STATUS_OK;
+ header.creds.token= hs->token;
+
+ /* construct the irpc packet */
+ ndr = ndr_push_init_ctx(state->irpc);
+ if (tevent_req_nomem(ndr, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_irpc_header(ndr, NDR_SCALARS|NDR_BUFFERS, &header);
+ status = ndr_map_error2ntstatus(ndr_err);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_bytes(ndr, in_data, in_length);
+ status = ndr_map_error2ntstatus(ndr_err);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ /* and send it */
+ state->in_packet = ndr_push_blob(ndr);
+ status = imessaging_send(hs->msg_ctx, hs->server_id,
+ MSG_IRPC, &state->in_packet);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ if (hs->timeout != IRPC_CALL_TIMEOUT_INF) {
+ /* set timeout-callback in case caller wants that */
+ ok = tevent_req_set_endtime(req, ev, timeval_current_ofs(hs->timeout, 0));
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ return req;
+}
+
+static void irpc_bh_raw_call_incoming_handler(struct irpc_request *irpc,
+ struct irpc_message *m)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(irpc->incoming.private_data,
+ struct tevent_req);
+ struct irpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct irpc_bh_raw_call_state);
+
+ talloc_steal(state, m);
+
+ if (!NT_STATUS_IS_OK(m->header.status)) {
+ tevent_req_nterror(req, m->header.status);
+ return;
+ }
+
+ state->out_data = data_blob_talloc(state,
+ m->ndr->data + m->ndr->offset,
+ m->ndr->data_size - m->ndr->offset);
+ if ((m->ndr->data_size - m->ndr->offset) > 0 && !state->out_data.data) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS irpc_bh_raw_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **out_data,
+ size_t *out_length,
+ uint32_t *out_flags)
+{
+ struct irpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct irpc_bh_raw_call_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *out_data = talloc_move(mem_ctx, &state->out_data.data);
+ *out_length = state->out_data.length;
+ *out_flags = 0;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct irpc_bh_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *irpc_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ struct tevent_req *req;
+ struct irpc_bh_disconnect_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct irpc_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ok = irpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ hs->msg_ctx = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS irpc_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;
+}
+
+static bool irpc_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+ return true;
+}
+
+static const struct dcerpc_binding_handle_ops irpc_bh_ops = {
+ .name = "wbint",
+ .is_connected = irpc_bh_is_connected,
+ .set_timeout = irpc_bh_set_timeout,
+ .raw_call_send = irpc_bh_raw_call_send,
+ .raw_call_recv = irpc_bh_raw_call_recv,
+ .disconnect_send = irpc_bh_disconnect_send,
+ .disconnect_recv = irpc_bh_disconnect_recv,
+
+ .ref_alloc = irpc_bh_ref_alloc,
+};
+
+/* initialise a irpc binding handle */
+struct dcerpc_binding_handle *irpc_binding_handle(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ struct irpc_bh_state *hs;
+
+ h = dcerpc_binding_handle_create(mem_ctx,
+ &irpc_bh_ops,
+ NULL,
+ table,
+ &hs,
+ struct irpc_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+ hs->msg_ctx = msg_ctx;
+ hs->server_id = server_id;
+ hs->table = table;
+ hs->timeout = IRPC_CALL_TIMEOUT;
+
+ dcerpc_binding_handle_set_sync_ev(h, msg_ctx->event.ev);
+
+ return h;
+}
+
+struct dcerpc_binding_handle *irpc_binding_handle_by_name(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ const char *dest_task,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ struct server_id *sids;
+ struct server_id sid;
+
+ /* find the server task */
+ sids = irpc_servers_byname(msg_ctx, mem_ctx, dest_task);
+ if (sids == NULL) {
+ errno = EADDRNOTAVAIL;
+ return NULL;
+ }
+ if (server_id_is_disconnected(&sids[0])) {
+ talloc_free(sids);
+ errno = EADDRNOTAVAIL;
+ return NULL;
+ }
+ sid = sids[0];
+ talloc_free(sids);
+
+ h = irpc_binding_handle(mem_ctx, msg_ctx,
+ sid, table);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ return h;
+}
+
+void irpc_binding_handle_add_security_token(struct dcerpc_binding_handle *h,
+ struct security_token *token)
+{
+ struct irpc_bh_state *hs =
+ dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+
+ hs->token = token;
+}