/* perform the send side of a async dcerpc request */ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, const struct GUID *object, uint16_t opnum, bool async, DATA_BLOB *stub_data) { struct rpc_request *req; p->conn->transport.recv_data = dcerpc_recv_data; req = talloc(p, 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->async_call = async; 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)); if (req->object == NULL) { 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; } DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *); talloc_set_destructor(req, dcerpc_req_dequeue); dcerpc_ship_next_request(p->conn); if (p->request_timeout) { event_add_timed(dcerpc_event_context(p), req, timeval_current_ofs(p->request_timeout, 0), dcerpc_timeout_handler, req); } return req; }
/* perform the receive side of a async dcerpc request */ 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 = dcerpc_event_context(req->p); if (event_loop_once(ctx) != 0) { return NT_STATUS_CONNECTION_DISCONNECTED; } } *stub_data = req->payload; status = req->status; if (stub_data->data) { stub_data->data = talloc_steal(mem_ctx, stub_data->data); } if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { req->p->last_fault_code = req->fault_code; } talloc_unlink(talloc_parent(req), req); return status; }
/* test the TestSleep interface */ static bool test_sleep(struct torture_context *tctx, struct dcerpc_pipe *p) { int i; NTSTATUS status; #define ASYNC_COUNT 3 struct rpc_request *req[ASYNC_COUNT]; struct echo_TestSleep r[ASYNC_COUNT]; bool done[ASYNC_COUNT]; struct timeval snd[ASYNC_COUNT]; struct timeval rcv[ASYNC_COUNT]; struct timeval diff[ASYNC_COUNT]; struct tevent_context *ctx; int total_done = 0; if (torture_setting_bool(tctx, "quick", false)) { torture_skip(tctx, "TestSleep disabled - use \"torture:quick=no\" to enable\n"); } torture_comment(tctx, "Testing TestSleep - use \"torture:quick=yes\" to disable\n"); for (i=0;i<ASYNC_COUNT;i++) { done[i] = false; snd[i] = timeval_current(); rcv[i] = timeval_zero(); r[i].in.seconds = ASYNC_COUNT-i; req[i] = dcerpc_echo_TestSleep_send(p, tctx, &r[i]); torture_assert(tctx, req[i], "Failed to send async sleep request\n"); } ctx = dcerpc_event_context(p); while (total_done < ASYNC_COUNT) { torture_assert(tctx, event_loop_once(ctx) == 0, "Event context loop failed"); for (i=0;i<ASYNC_COUNT;i++) { if (done[i] == false && req[i]->state == RPC_REQUEST_DONE) { int rounded_tdiff; total_done++; done[i] = true; rcv[i] = timeval_current(); diff[i] = timeval_until(&snd[i], &rcv[i]); rounded_tdiff = (int)(0.5 + diff[i].tv_sec + (1.0e-6*diff[i].tv_usec)); status = dcerpc_ndr_request_recv(req[i]); torture_comment(tctx, "rounded_tdiff=%d\n", rounded_tdiff); torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "TestSleep(%d) failed", i)); torture_assert(tctx, r[i].out.result == r[i].in.seconds, talloc_asprintf(tctx, "Failed - Asked to sleep for %u seconds (server replied with %u seconds and the reply takes only %u seconds)", r[i].out.result, r[i].in.seconds, (unsigned int)diff[i].tv_sec)); torture_assert(tctx, r[i].out.result <= rounded_tdiff, talloc_asprintf(tctx, "Failed - Slept for %u seconds (but reply takes only %u.%06u seconds)", r[i].out.result, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec)); if (r[i].out.result+1 == rounded_tdiff) { torture_comment(tctx, "Slept for %u seconds (but reply takes %u.%06u seconds - busy server?)\n", r[i].out.result, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec); } else if (r[i].out.result == rounded_tdiff) { torture_comment(tctx, "Slept for %u seconds (reply takes %u.%06u seconds - ok)\n", r[i].out.result, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec); } else { torture_comment(tctx, "(Failed) - Not async - Slept for %u seconds (but reply takes %u.%06u seconds)", r[i].out.result, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec); /* TODO: let the test fail here, when we support async rpc on ncacn_np */ } } } } torture_comment(tctx, "\n"); return true; }