#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "librpc/gen_ndr/ndr_misc.h"
+static void dcerpc_ship_next_request(struct dcerpc_connection *c);
+
static struct dcerpc_interface_list *dcerpc_pipes = NULL;
/*
if (req->async.callback) {
req->async.callback(req);
}
+
+ if (c->request_queue != NULL) {
+ dcerpc_ship_next_request(c);
+ }
}
/*
/*
perform the send side of a async dcerpc request
*/
-struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
- const struct GUID *object,
- uint16_t opnum,
- DATA_BLOB *stub_data)
+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;
- struct ncacn_packet pkt;
- DATA_BLOB blob;
- uint32_t remaining, chunk_size;
- BOOL first_packet = True;
p->conn->transport.recv_data = dcerpc_request_recv_data;
req->payload = data_blob(NULL, 0);
req->flags = 0;
req->fault_code = 0;
+ req->async_call = async;
req->async.callback = NULL;
+ if (object != NULL) {
+ req->object = talloc_memdup(req, 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.data == NULL) {
+ return NULL;
+ }
+
+ DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+
+ 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);
+ }
+
+ talloc_set_destructor(req, dcerpc_req_destructor);
+ return req;
+}
+
+/*
+ Send a request using the transport
+*/
+
+static void dcerpc_ship_next_request(struct dcerpc_connection *c)
+{
+ struct rpc_request *req;
+ struct dcerpc_pipe *p;
+ DATA_BLOB *stub_data;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ uint32_t remaining, chunk_size;
+ BOOL first_packet = True;
+
+ req = c->request_queue;
+ if (req == NULL) {
+ return;
+ }
+
+ p = req->p;
+ stub_data = &req->request_data;
+
+ if (!req->async_call && (c->pending != NULL)) {
+ return;
+ }
+
+ DLIST_REMOVE(c->request_queue, req);
+ DLIST_ADD(c->pending, req);
+
init_ncacn_hdr(p->conn, &pkt);
remaining = stub_data->length;
pkt.pfc_flags = 0;
pkt.u.request.alloc_hint = remaining;
pkt.u.request.context_id = p->context_id;
- pkt.u.request.opnum = opnum;
+ pkt.u.request.opnum = req->opnum;
- if (object) {
- pkt.u.request.object.object = *object;
+ if (req->object) {
+ pkt.u.request.object.object = *req->object;
pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
- chunk_size -= ndr_size_GUID(object,0);
+ chunk_size -= ndr_size_GUID(req->object,0);
}
- DLIST_ADD(p->conn->pending, req);
-
/* we send a series of pdus without waiting for a reply */
while (remaining > 0 || first_packet) {
uint32_t chunk = MIN(chunk_size, remaining);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
DLIST_REMOVE(p->conn->pending, req);
- return req;
+ return;
}
req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
DLIST_REMOVE(p->conn->pending, req);
- return req;
+ return;
}
remaining -= chunk;
}
-
- if (p->request_timeout) {
- event_add_timed(dcerpc_event_context(p), req,
- timeval_current_ofs(p->request_timeout, 0),
- dcerpc_timeout_handler, req);
- }
-
- talloc_set_destructor(req, dcerpc_req_destructor);
-
- return req;
}
/*
NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
struct GUID *object,
uint16_t opnum,
+ BOOL async,
TALLOC_CTX *mem_ctx,
DATA_BLOB *stub_data_in,
DATA_BLOB *stub_data_out)
{
struct rpc_request *req;
- req = dcerpc_request_send(p, object, opnum, stub_data_in);
+ req = dcerpc_request_send(p, object, opnum, async, stub_data_in);
if (req == NULL) {
return NT_STATUS_NO_MEMORY;
}
dump_data(10, request.data, request.length);
/* make the actual dcerpc request */
- req = dcerpc_request_send(p, object, opnum, &request);
+ req = dcerpc_request_send(p, object, opnum, table->calls[opnum].async,
+ &request);
if (req != NULL) {
req->ndr.table = table;
void (*recv_data)(struct dcerpc_connection *, DATA_BLOB *, NTSTATUS status);
} transport;
- /* pending requests */
+ /* Requests that have been sent, waiting for a reply */
struct rpc_request *pending;
+ /* Sync requests waiting to be shipped */
+ struct rpc_request *request_queue;
+
/* private pointer for pending full requests */
void *full_request_private;
ndr_push_flags_fn_t ndr_push;
ndr_pull_flags_fn_t ndr_pull;
ndr_print_function_t ndr_print;
+ BOOL async;
};
struct dcerpc_endpoint_list {
uint_t flags;
uint32_t fault_code;
+ const struct GUID *object;
+ uint16_t opnum;
+ DATA_BLOB request_data;
+ BOOL async_call;
+
/* use by the ndr level async recv call */
struct {
const struct dcerpc_interface_table *table;
$rettype = $d->{RETURN_TYPE};
}
+ my $async = 0;
+ if (has_property($d, "async")) { $async = 1; }
+
return {
NAME => $d->{NAME},
TYPE => "FUNCTION",
OPNUM => $thisopnum,
+ ASYNC => $async,
RETURN_TYPE => $rettype,
PROPERTIES => $d->{PROPERTIES},
ELEMENTS => \@elements,
"noopnum" => ["FUNCTION"],
"in" => ["ELEMENT"],
"out" => ["ELEMENT"],
+ "async" => ["FUNCTION"],
# pointer
"ref" => ["ELEMENT"],
pidl "\t\tsizeof(struct $d->{NAME}),";
pidl "\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},";
pidl "\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},";
- pidl "\t\t(ndr_print_function_t) ndr_print_$d->{NAME}";
+ pidl "\t\t(ndr_print_function_t) ndr_print_$d->{NAME},";
+ pidl "\t\t".($d->{ASYNC}?"True":"False").",";
pidl "\t},";
$count++;
}
- pidl "\t{ NULL, 0, NULL, NULL, NULL }";
+ pidl "\t{ NULL, 0, NULL, NULL, NULL, False }";
pidl "};";
pidl "";
memcpy(stub_in.data, base_in->data, insert_ofs);
memcpy(stub_in.data+insert_ofs+n, base_in->data+insert_ofs, base_in->length-insert_ofs);
- status = dcerpc_request(p, NULL, opnum, mem_ctx, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, opnum, False, mem_ctx, &stub_in, &stub_out);
if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
print_depth(depth);
/* work out which elements are pointers */
for (ofs=min_ofs;ofs<=max_ofs-4;ofs+=4) {
SIVAL(stub_in.data, ofs, 1);
- status = dcerpc_request(p, NULL, opnum, mem_ctx, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, opnum, False, mem_ctx, &stub_in, &stub_out);
if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
print_depth(depth);
data_blob_clear(&stub_in);
- status = dcerpc_request(p, NULL, opnum, mem_ctx, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, opnum, False, mem_ctx, &stub_in, &stub_out);
if (NT_STATUS_IS_OK(status)) {
printf("opnum %d min_input %d - output %d\n",
fill_blob_handle(&stub_in, mem_ctx, &handle);
- status = dcerpc_request(p, NULL, opnum, mem_ctx, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, opnum, False, mem_ctx, &stub_in, &stub_out);
if (NT_STATUS_IS_OK(status)) {
printf("opnum %d min_input %d - output %d (with handle)\n",
printf("\nScanning pipe '%s'\n", iface->name);
for (i=0;i<5000;i++) {
- status = dcerpc_request(p, NULL, i, p, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, i, False, p, &stub_in, &stub_out);
if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) &&
p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) break;
if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
#include "includes.h"
#include "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/events/events.h"
static void init_lsa_String(struct lsa_String *name, const char *s)
{
r.out.names = &names;
status = dcerpc_lsa_LookupSids(p, mem_ctx, &r);
- if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
printf("LookupSids failed - %s\n", nt_errstr(status));
return False;
}
return True;
}
+#define NUM_ASYNC_REQUESTS 1000
+
+static void lookupsids_cb(struct rpc_request *req)
+{
+ int *replies = (int *)req->async.private;
+ NTSTATUS status;
+
+ status = dcerpc_ndr_request_recv(req);
+ DEBUG(3, ("lookupsids returned %s\n", nt_errstr(status)));
+ if (!NT_STATUS_IS_OK(status)) {
+ *replies = -1;
+ }
+
+ *replies += 1;
+}
+
+static BOOL test_LookupSids_async(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_SidArray sids;
+ struct lsa_SidPtr sidptr;
+
+ uint32_t count[NUM_ASYNC_REQUESTS];
+ struct lsa_TransNameArray names[NUM_ASYNC_REQUESTS];
+ struct lsa_LookupSids r[NUM_ASYNC_REQUESTS];
+ struct rpc_request **req;
+
+ int i, replies;
+ BOOL ret = True;
+
+ printf("\nTesting %d async lookupsids request\n", 100);
+
+ req = talloc_array(mem_ctx, struct rpc_request *, NUM_ASYNC_REQUESTS);
+
+ sids.num_sids = 1;
+ sids.sids = &sidptr;
+ sidptr.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-545");
+
+ replies = 0;
+
+ for (i=0; i<NUM_ASYNC_REQUESTS; i++) {
+ count[i] = 0;
+ names[i].count = 0;
+ names[i].names = NULL;
+
+ r[i].in.handle = handle;
+ r[i].in.sids = &sids;
+ r[i].in.names = &names[i];
+ r[i].in.level = 1;
+ r[i].in.count = &names[i].count;
+ r[i].out.count = &count[i];
+ r[i].out.names = &names[i];
+
+ req[i] = dcerpc_lsa_LookupSids_send(p, req, &r[i]);
+ if (req[i] == NULL) {
+ ret = False;
+ break;
+ }
+
+ req[i]->async.callback = lookupsids_cb;
+ req[i]->async.private = &replies;
+ }
+
+ while (replies < NUM_ASYNC_REQUESTS) {
+ event_loop_once(p->conn->event_ctx);
+ if (replies < 0) {
+ ret = False;
+ break;
+ }
+ }
+
+ talloc_free(req);
+
+ return ret;
+}
+
static BOOL test_LookupPrivValue(struct dcerpc_pipe *p,
TALLOC_CTX *mem_ctx,
struct policy_handle *handle,
}
if (handle) {
+ if (!test_LookupSids_async(p, mem_ctx, handle)) {
+ ret = False;
+ }
+
if (!test_QueryDomainInfoPolicy(p, mem_ctx, handle)) {
ret = False;
}
}
}
-
-
talloc_free(mem_ctx);
return ret;
memset(stub_in.data, 0xFF, stub_in.length);
for (i=0;i<200;i++) {
- status = dcerpc_request(p, NULL, i, mem_ctx, &stub_in, &stub_out);
+ status = dcerpc_request(p, NULL, False, i, mem_ctx, &stub_in, &stub_out);
if (!NT_STATUS_IS_OK(status) &&
p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
break;