From fae215266b6711b24f4893653b146751885e4e5f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Jan 2005 16:53:02 +0000 Subject: r4690: - add support for async rpc server replies the backend should check for (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC) then it's allowed to reply async then the backend should mark that call as async with dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; later it has to manualy set r->out.result and then send the reply by calling status = dcesrv_reply(p->dce_call); NOTE: that ncacn_np doesn't support async replies yet - implement an async version of echo_TestSleep - reenable the echo_TestSleep torture test (this need to be more strict when we have support for async ncacn_np) metze (This used to be commit f0a0dbeb25b034b1333078ca085999359f5f6209) --- source4/build/pidl/server.pm | 73 +++++++++--- source4/build/pidl/stub.pm | 82 ++++++++++---- source4/ntvfs/ipc/vfs_ipc.c | 49 ++++---- source4/rpc_server/dcerpc_server.c | 221 +++++++++++++++++++++---------------- source4/rpc_server/dcerpc_server.h | 41 ++++++- source4/rpc_server/dcerpc_sock.c | 18 +-- source4/rpc_server/echo/rpc_echo.c | 63 ++++++++++- source4/torture/rpc/echo.c | 49 +++++--- 8 files changed, 410 insertions(+), 186 deletions(-) (limited to 'source4') diff --git a/source4/build/pidl/server.pm b/source4/build/pidl/server.pm index 5c70adf2af..e080bf5654 100644 --- a/source4/build/pidl/server.pm +++ b/source4/build/pidl/server.pm @@ -36,6 +36,29 @@ sub gen_dispatch_switch($) } else { pidl "\t\t$d->{NAME}(dce_call, mem_ctx, r2);\n"; } + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} will reply async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + $count++; + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my $data = shift; + + my $count = 0; + foreach my $d (@{$data}) { + next if ($d->{TYPE} ne "FUNCTION"); + + pidl "\tcase $count: {\n"; + pidl "\t\tstruct $d->{NAME} *r2 = r;\n"; + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} replied async\\n\"));\n"; + pidl "\t\t}\n"; pidl "\t\tif (DEBUGLEVEL > 10 && dce_call->fault_code == 0) {\n"; pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; pidl "\t\t}\n"; @@ -47,7 +70,6 @@ sub gen_dispatch_switch($) } } - ##################################################################### # produce boilerplate code for a interface sub Boilerplate_Iface($) @@ -91,9 +113,7 @@ static NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_C } *r = talloc_size(mem_ctx, dcerpc_table_$name.calls[opnum].struct_size); - if (!*r) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(*r); /* unravel the NDR for the packet */ status = dcerpc_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r); @@ -111,8 +131,6 @@ static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_C { uint16 opnum = dce_call->pkt.u.request.opnum; - dce_call->fault_code = 0; - switch (opnum) { "; gen_dispatch_switch($data); @@ -132,6 +150,29 @@ pidl " return NT_STATUS_OK; } +static NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16 opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_reply_switch($data); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + dcerpc_log_packet(&dcerpc_table_$name, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, void *r) { NTSTATUS status; @@ -147,14 +188,15 @@ static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_C } static const struct dcesrv_interface $name\_interface = { - \"$name\", - $uuid, - $if_version, - $name\__op_bind, - $name\__op_unbind, - $name\__op_ndr_pull, - $name\__op_dispatch, - $name\__op_ndr_push + .name = \"$name\", + .uuid = $uuid, + .if_version = $if_version, + .bind = $name\__op_bind, + .unbind = $name\__op_unbind, + .ndr_pull = $name\__op_ndr_pull, + .dispatch = $name\__op_dispatch, + .reply = $name\__op_reply, + .ndr_push = $name\__op_ndr_push }; "; @@ -237,7 +279,7 @@ NTSTATUS dcerpc_server_$name\_init(void) } ##################################################################### -# parse a parsed IDL structure back into an IDL file +# dcerpc server boilerplate from a parsed IDL structure sub ParseInterface($) { my($interface) = shift; @@ -270,4 +312,3 @@ sub ParseInterface($) } 1; - diff --git a/source4/build/pidl/stub.pm b/source4/build/pidl/stub.pm index d1448d467b..a9c938df63 100644 --- a/source4/build/pidl/stub.pm +++ b/source4/build/pidl/stub.pm @@ -2,6 +2,7 @@ # stub boilerplate generator # Copyright jelmer@samba.org 2004 # Copyright tridge@samba.org 2003 +# Copyright metze@samba.org 2004 # released under the GNU GPL package IdlStub; @@ -39,25 +40,46 @@ sub gen_dispatch_switch($) } else { pidl "\t\tvtable->$d->{NAME}(iface, mem_ctx, r2);\n"; } + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} will reply async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + $count++; + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my $data = shift; + + my $count = 0; + foreach my $d (@{$data}) { + next if ($d->{TYPE} ne "FUNCTION"); + + pidl "\tcase $count: {\n"; + pidl "\t\tstruct $d->{NAME} *r2 = r;\n"; + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} replied async\\n\"));\n"; + pidl "\t\t}\n"; pidl "\t\tif (DEBUGLEVEL > 10 && dce_call->fault_code == 0) {\n"; pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; pidl "\t\t}\n"; pidl "\t\tif (dce_call->fault_code != 0) {\n"; - pidl "\t\t\tDEBUG(2,(\"dcerpc_fault 0x%x in $d->{NAME}\\n\", dce_call->fault_code));\n"; + pidl "\t\t\tDEBUG(2,(\"dcerpc_fault %s in $d->{NAME}\\n\", dcerpc_errstr(mem_ctx, dce_call->fault_code)));\n"; pidl "\t\t}\n"; pidl "\t\tbreak;\n\t}\n"; $count++; } } - ##################################################################### # produce boilerplate code for a interface sub Boilerplate_Iface($) { my($interface) = shift; my($data) = $interface->{DATA}; - my $count = 0; my $name = $interface->{NAME}; my $uname = uc $name; my $uuid = util::make_str($interface->{PROPERTIES}->{uuid}); @@ -76,7 +98,7 @@ static NTSTATUS $name\__op_bind(struct dcesrv_call_state *dce_call, const struct static void $name\__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) { #ifdef DCESRV_INTERFACE_$uname\_UNBIND - DCESRV_INTERFACE_$uname\_UNBIND(context,iface); + DCESRV_INTERFACE_$uname\_UNBIND(context, iface); #else return; #endif @@ -95,11 +117,9 @@ static NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_C } *r = talloc_size(mem_ctx, dcerpc_table_$name.calls[opnum].struct_size); - if (!*r) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(*r); - /* unravel the NDR for the packet */ + /* unravel the NDR for the packet */ status = dcerpc_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r); if (!NT_STATUS_IS_OK(status)) { dcerpc_log_packet(&dcerpc_table_$name, opnum, NDR_IN, @@ -118,8 +138,6 @@ static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_C struct dcom_interface_p *iface = dcom_get_local_iface_p(&ipid); const struct dcom_$name\_vtable *vtable = iface->vtable; - dce_call->fault_code = 0; - switch (opnum) { "; gen_dispatch_switch($data); @@ -139,6 +157,29 @@ pidl " return NT_STATUS_OK; } +static NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16 opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_reply_switch($data); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + dcerpc_log_packet(&dcerpc_table_$name, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, void *r) { NTSTATUS status; @@ -154,14 +195,15 @@ static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_C } static const struct dcesrv_interface $name\_interface = { - \"$name\", - $uuid, - $if_version, - $name\__op_bind, - $name\__op_unbind, - $name\__op_ndr_pull, - $name\__op_dispatch, - $name\__op_ndr_push + .name = \"$name\", + .uuid = $uuid, + .if_version = $if_version, + .bind = $name\__op_bind, + .unbind = $name\__op_unbind, + .ndr_pull = $name\__op_ndr_pull, + .dispatch = $name\__op_dispatch, + .reply = $name\__op_reply, + .ndr_push = $name\__op_ndr_push }; "; @@ -243,10 +285,12 @@ NTSTATUS dcerpc_server_$name\_init(void) "; } +##################################################################### +# dcom interface stub from a parsed IDL structure sub ParseInterface($) { my($interface) = shift; - my($data) = $interface->{DATA}; + my($data) = $interface->{DATA}; my $count = 0; $res = ""; diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c index 2a19de1ec0..402d1ead64 100644 --- a/source4/ntvfs/ipc/vfs_ipc.c +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -3,7 +3,7 @@ default IPC$ NTVFS backend Copyright (C) Andrew Tridgell 2003 - Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Stefan (metze) Metzmacher 2004-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 @@ -94,7 +94,7 @@ static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs, NT_STATUS_HAVE_NO_MEMORY(private->idtree_fnum); /* setup the DCERPC server subsystem */ - status = dcesrv_init_context(private, &private->dcesrv); + status = dcesrv_init_ipc_context(private, &private->dcesrv); NT_STATUS_NOT_OK_RETURN(status); return NT_STATUS_OK; @@ -106,13 +106,6 @@ static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs, static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs, struct smbsrv_tcon *tcon) { - struct ipc_private *private = ntvfs->private_data; - - /* close any pipes that are open. Discard any unread data */ - while (private->pipe_list) { - talloc_free(private->pipe_list); - } - return NT_STATUS_OK; } @@ -171,7 +164,6 @@ static int ipc_fd_destructor(void *ptr) struct pipe_state *p = ptr; idr_remove(p->private->idtree_fnum, p->fnum); DLIST_REMOVE(p->private->pipe_list, p); - talloc_free(p->dce_conn); return 0; } @@ -186,21 +178,21 @@ static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs, struct pipe_state *p; NTSTATUS status; struct dcerpc_binding ep_description; - struct auth_session_info *session_info = NULL; struct ipc_private *private = ntvfs->private_data; int fnum; + struct server_connection *srv_conn; - p = talloc_p(req, struct pipe_state); - if (!p) { - return NT_STATUS_NO_MEMORY; + if (!req->session || !req->session->session_info) { + return NT_STATUS_ACCESS_DENIED; } + p = talloc(req, struct pipe_state); + NT_STATUS_HAVE_NO_MEMORY(p); + while (fname[0] == '\\') fname++; p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname); - if (!p->pipe_name) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(p->pipe_name); fnum = idr_get_new_above(private->idtree_fnum, p, IPC_BASE_FNUM, UINT16_MAX); if (fnum == -1) { @@ -215,24 +207,23 @@ static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs, endpoint. At this stage the pipe isn't bound, so we don't know what interface the user actually wants, just that they want one of the interfaces attached to this pipe endpoint. - - TODO: note that we aren't passing any credentials here. We - will need to do that once the credentials infrastructure is - finalised for Samba4 */ ep_description.transport = NCACN_NP; ep_description.endpoint = p->pipe_name; - /* tell the RPC layer the session_info */ - if (req->session) { - /* The session info is refcount-increased in the - dcesrv_endpoint_search_connect() function */ - session_info = req->session->session_info; - } + /* TOTO: pass in full server_connection in here */ + srv_conn = talloc_zero(p, struct server_connection); + NT_STATUS_HAVE_NO_MEMORY(srv_conn); + srv_conn->event.ctx = talloc_reference(srv_conn, req->smb_conn->connection->event.ctx); - status = dcesrv_endpoint_search_connect(private->dcesrv, + /* The session info is refcount-increased in the + * dcesrv_endpoint_search_connect() function + */ + status = dcesrv_endpoint_search_connect(private->dcesrv, + p, &ep_description, - session_info, + req->session->session_info, + srv_conn, &p->dce_conn); if (!NT_STATUS_IS_OK(status)) { idr_remove(private->idtree_fnum, p->fnum); diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 925004b191..f031bcb50b 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -3,8 +3,8 @@ server side dcerpc core code - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) Stefan (metze) Metzmacher 2004-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 @@ -27,6 +27,7 @@ #include "auth/auth.h" #include "dlinklist.h" #include "rpc_server/dcerpc_server.h" +#include "events.h" /* see if two endpoints match @@ -286,10 +287,8 @@ static int dcesrv_endpoint_destructor(void *ptr) if (c->iface) { c->iface->unbind(c, c->iface); } - talloc_free(c); } - return 0; } @@ -298,28 +297,32 @@ static int dcesrv_endpoint_destructor(void *ptr) connect to a dcerpc endpoint */ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, + TALLOC_CTX *mem_ctx, const struct dcesrv_endpoint *ep, - struct dcesrv_connection **p) + struct server_connection *srv_conn, + struct dcesrv_connection **_p) { - *p = talloc_p(dce_ctx, struct dcesrv_connection); - if (! *p) { - return NT_STATUS_NO_MEMORY; - } - - (*p)->dce_ctx = dce_ctx; - (*p)->endpoint = ep; - (*p)->contexts = NULL; - (*p)->call_list = NULL; - (*p)->cli_max_recv_frag = 0; - (*p)->partial_input = data_blob(NULL, 0); - (*p)->auth_state.auth_info = NULL; - (*p)->auth_state.gensec_security = NULL; - (*p)->auth_state.session_info = NULL; - (*p)->auth_state.session_key = dcesrv_generic_session_key; - (*p)->srv_conn = NULL; - - talloc_set_destructor(*p, dcesrv_endpoint_destructor); - + struct dcesrv_connection *p; + + p = talloc(mem_ctx, struct dcesrv_connection); + NT_STATUS_HAVE_NO_MEMORY(p); + + p->dce_ctx = dce_ctx; + p->endpoint = ep; + p->contexts = NULL; + p->call_list = NULL; + p->pending_call_list = NULL; + p->cli_max_recv_frag = 0; + p->partial_input = data_blob(NULL, 0); + p->auth_state.auth_info = NULL; + p->auth_state.gensec_security = NULL; + p->auth_state.session_info = NULL; + p->auth_state.session_key = dcesrv_generic_session_key; + p->srv_conn = srv_conn; + + talloc_set_destructor(p, dcesrv_endpoint_destructor); + + *_p = p; return NT_STATUS_OK; } @@ -327,8 +330,10 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, search and connect to a dcerpc endpoint */ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx, + TALLOC_CTX *mem_ctx, const struct dcerpc_binding *ep_description, struct auth_session_info *session_info, + struct server_connection *srv_conn, struct dcesrv_connection **dce_conn_p) { NTSTATUS status; @@ -340,7 +345,7 @@ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx, return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - status = dcesrv_endpoint_connect(dce_ctx, ep, dce_conn_p); + status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -730,26 +735,24 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) { struct ndr_pull *pull; - struct ndr_push *push; - void *r; NTSTATUS status; - DATA_BLOB stub; - uint32_t total_length; struct dcesrv_connection_context *context; - call->fault_code = 0; + call->fault_code = 0; + call->state_flags = call->conn->dce_ctx->state_flags; + call->time = timeval_current(); context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id); if (context == NULL) { return dcesrv_fault(call, DCERPC_FAULT_UNK_IF); } - call->context = context; - pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call); - if (!pull) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(pull); + + call->context = context; + call->event_ctx = context->conn->srv_conn->event.ctx; + call->ndr_pull = pull; if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) { pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT; @@ -760,7 +763,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) } /* unravel the NDR for the packet */ - status = context->iface->ndr_pull(call, call, pull, &r); + status = context->iface->ndr_pull(call, call, pull, &call->r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, call->fault_code); } @@ -772,27 +775,49 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) } /* call the dispatch function */ - status = context->iface->dispatch(call, call, r); + status = context->iface->dispatch(call, call, call->r); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault(call, call->fault_code); + } + + /* add the call to the pending list */ + DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *); + + if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { + return NT_STATUS_OK; + } + + return dcesrv_reply(call); +} + +NTSTATUS dcesrv_reply(struct dcesrv_call_state *call) +{ + struct ndr_push *push; + NTSTATUS status; + DATA_BLOB stub; + uint32_t total_length; + struct dcesrv_connection_context *context = call->context; + + /* call the reply function */ + status = context->iface->reply(call, call, call->r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, call->fault_code); } /* form the reply NDR */ push = ndr_push_init_ctx(call); - if (!push) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(push); /* carry over the pointer count to the reply in case we are using full pointer. See NDR specification for full pointers */ - push->ptr_count = pull->ptr_count; + push->ptr_count = call->ndr_pull->ptr_count; if (lp_rpc_big_endian()) { push->flags |= LIBNDR_FLAG_BIGENDIAN; } - status = context->iface->ndr_push(call, call, push, r); + status = context->iface->ndr_push(call, call, push, call->r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, call->fault_code); } @@ -807,9 +832,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) struct dcerpc_packet pkt; rep = talloc(call, struct dcesrv_call_reply); - if (!rep) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY(rep); length = stub.length; if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) { @@ -848,8 +871,17 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) stub.length -= length; } while (stub.length != 0); + /* move the call from the pending to the finished calls list */ + DLIST_REMOVE(call->conn->pending_call_list, call); DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *); + if (call->conn->call_list && call->conn->call_list->replies) { + if (call->conn->srv_conn && + call->conn->srv_conn->event.fde) { + call->conn->srv_conn->event.fde->flags |= EVENT_FD_WRITE; + } + } + return NT_STATUS_OK; } @@ -1074,6 +1106,13 @@ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, call = dce_conn->call_list; if (!call || !call->replies) { + if (dce_conn->pending_call_list) { + /* TODO: we need to say act async here + * as we know we have pending requests + * which will be finished at a time + */ + return NT_STATUS_FOOBAR; + } return NT_STATUS_FOOBAR; } rep = call->replies; @@ -1128,83 +1167,73 @@ NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn, return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn); } -/* - initialise the dcerpc server context -*/ -NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **dce_ctx) +static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx) { + NTSTATUS status; + struct dcesrv_context *dce_ctx; int i; - const char **endpoint_servers = lp_dcerpc_endpoint_servers(); - (*dce_ctx) = talloc_p(mem_ctx, struct dcesrv_context); - if (! *dce_ctx) { - return NT_STATUS_NO_MEMORY; - } - - (*dce_ctx)->endpoint_list = NULL; + DEBUG(1,("dcesrv_init\n")); if (!endpoint_servers) { - DEBUG(3,("dcesrv_init_context: no endpoint servers configured\n")); - return NT_STATUS_OK; + DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n")); + return NT_STATUS_INTERNAL_ERROR; } + dce_ctx = talloc(mem_ctx, struct dcesrv_context); + NT_STATUS_HAVE_NO_MEMORY(dce_ctx); + dce_ctx->endpoint_list = NULL; + dce_ctx->state_flags = state_flags; + for (i=0;endpoint_servers[i];i++) { - NTSTATUS ret; const struct dcesrv_endpoint_server *ep_server; - + ep_server = dcesrv_ep_server_byname(endpoint_servers[i]); if (!ep_server) { DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i])); - return NT_STATUS_UNSUCCESSFUL; + return NT_STATUS_INTERNAL_ERROR; } - ret = ep_server->init_server(*dce_ctx, ep_server); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i])); - return ret; + status = ep_server->init_server(dce_ctx, ep_server); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i], + nt_errstr(status))); + return status; } } + *_dce_ctx = dce_ctx; return NT_STATUS_OK; } -static void dcesrv_init(struct server_service *service, const struct model_ops *model_ops) +/* + initialise the dcerpc server context +*/ +NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx) { + NTSTATUS status; struct dcesrv_context *dce_ctx; - int i; - const char **endpoint_servers = lp_dcerpc_endpoint_servers(); - DEBUG(1,("dcesrv_init\n")); + status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx); + NT_STATUS_NOT_OK_RETURN(status); - if (!endpoint_servers) { - DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n")); - return; - } - - dce_ctx = talloc_p(service, struct dcesrv_context); - if (!dce_ctx) { - DEBUG(0,("talloc_p(mem_ctx, struct dcesrv_context) failed\n")); - return; - } + *_dce_ctx = dce_ctx; + return NT_STATUS_OK; +} - ZERO_STRUCTP(dce_ctx); - dce_ctx->endpoint_list = NULL; +static void dcesrv_init(struct server_service *service, const struct model_ops *model_ops) +{ + NTSTATUS status; + struct dcesrv_context *dce_ctx; - for (i=0;endpoint_servers[i];i++) { - NTSTATUS ret; - const struct dcesrv_endpoint_server *ep_server; - - ep_server = dcesrv_ep_server_byname(endpoint_servers[i]); - if (!ep_server) { - DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i])); - return; - } + DEBUG(1,("dcesrv_init\n")); - ret = ep_server->init_server(dce_ctx, ep_server); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i])); - return; - } + status = dcesrv_init_context(service, + lp_dcerpc_endpoint_servers(), + DCESRV_CALL_STATE_FLAG_MAY_ASYNC, + &dce_ctx); + if (!NT_STATUS_IS_OK(status)) { + return; } dcesrv_sock_init(service, model_ops, dce_ctx); @@ -1330,7 +1359,7 @@ static const struct server_service_ops dcesrv_ops = { .send_handler = dcesrv_send, .idle_handler = NULL, .close_connection = dcesrv_close, - .service_exit = dcesrv_exit, + .service_exit = dcesrv_exit, }; const struct server_service_ops *dcesrv_get_ops(void) diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 49fbd4477b..2738cee040 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -3,8 +3,8 @@ server side dcerpc defines - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) Stefan (metze) Metzmacher 2004-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 @@ -54,6 +54,10 @@ struct dcesrv_interface { */ NTSTATUS (*dispatch)(struct dcesrv_call_state *, TALLOC_CTX *, void *); + /* the reply function for the chosen interface. + */ + NTSTATUS (*reply)(struct dcesrv_call_state *, TALLOC_CTX *, void *); + /* the ndr_push function for the chosen interface. */ NTSTATUS (*ndr_push)(struct dcesrv_call_state *, TALLOC_CTX *, struct ndr_push *,void *); @@ -69,6 +73,33 @@ struct dcesrv_call_state { struct dcesrv_connection_context *context; struct dcerpc_packet pkt; + /* the backend can mark the call + * with DCESRV_CALL_STATE_FLAG_ASYNC + * that will cause the frontend to not touch r->out + * and skip the reply + * + * this is only allowed to the backend when DCESRV_CALL_STATE_FLAG_MAY_ASYNC + * is alerady set by the frontend + * + * the backend then needs to call dcesrv_reply() when it's + * ready to send the reply + */ +#define DCESRV_CALL_STATE_FLAG_ASYNC (1<<0) +#define DCESRV_CALL_STATE_FLAG_MAY_ASYNC (1<<1) + uint32_t state_flags; + + /* the time the request arrived in the server */ + struct timeval time; + + /* the backend can use this event context for async replies */ + struct event_context *event_ctx; + + /* this is the pointer to the allocated function struct */ + void *r; + + /* that's the ndr push context used in dcesrv_request */ + struct ndr_pull *ndr_pull; + DATA_BLOB input; struct dcesrv_call_reply { @@ -132,6 +163,9 @@ struct dcesrv_connection { /* the state of the current calls */ struct dcesrv_call_state *call_list; + /* the state of the async pending calls */ + struct dcesrv_call_state *pending_call_list; + /* the maximum size the client wants to receive */ uint32_t cli_max_recv_frag; @@ -187,6 +221,9 @@ struct dcesrv_context { struct dcesrv_interface iface; } *interface_list; } *endpoint_list; + + /* this is the default state_flags for dcesrv_call_state structs */ + uint32_t state_flags; }; /* this structure is used by modules to determine the size of some critical types */ diff --git a/source4/rpc_server/dcerpc_sock.c b/source4/rpc_server/dcerpc_sock.c index 2155f97376..420c73cb1c 100644 --- a/source4/rpc_server/dcerpc_sock.c +++ b/source4/rpc_server/dcerpc_sock.c @@ -4,7 +4,7 @@ server side dcerpc using various kinds of sockets (tcp, unix domain) Copyright (C) Andrew Tridgell 2003 - Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Stefan (metze) Metzmacher 2004-2005 Copyright (C) Jelmer Vernooij 2004 This program is free software; you can redistribute it and/or modify @@ -48,7 +48,7 @@ static ssize_t dcerpc_write_fn(void *private, DATA_BLOB *out) return sendlen; } -void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) +static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) { server_terminate_connection(dce_conn->srv_conn, reason); } @@ -226,24 +226,26 @@ void dcesrv_sock_init(struct server_service *service, const struct model_ops *mo return; } -void dcesrv_sock_accept(struct server_connection *conn) +void dcesrv_sock_accept(struct server_connection *srv_conn) { NTSTATUS status; - struct dcesrv_socket_context *dcesrv_sock = conn->server_socket->private_data; + struct dcesrv_socket_context *dcesrv_sock = srv_conn->server_socket->private_data; struct dcesrv_connection *dcesrv_conn = NULL; DEBUG(5,("dcesrv_sock_accept\n")); - status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, dcesrv_sock->endpoint, &dcesrv_conn); + status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, + dcesrv_sock, + dcesrv_sock->endpoint, + srv_conn, + &dcesrv_conn); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n", nt_errstr(status))); return; } - dcesrv_conn->srv_conn = conn; - - conn->private_data = dcesrv_conn; + srv_conn->private_data = dcesrv_conn; return; } diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c index e5c1ee56ce..164e2d588f 100644 --- a/source4/rpc_server/echo/rpc_echo.c +++ b/source4/rpc_server/echo/rpc_echo.c @@ -4,6 +4,7 @@ endpoint server for the echo pipe Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 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 @@ -23,6 +24,7 @@ #include "includes.h" #include "rpc_server/dcerpc_server.h" #include "librpc/gen_ndr/ndr_echo.h" +#include "events.h" static NTSTATUS echo_AddOne(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_AddOne *r) @@ -112,10 +114,67 @@ static NTSTATUS echo_TestEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *me return NT_STATUS_OK; } +struct echo_TestSleep_private { + struct dcesrv_call_state *dce_call; + struct echo_TestSleep *r; + struct timed_event *te; +}; + +static void echo_TestSleep_handler(struct event_context *ev, struct timed_event *te, struct timeval t) +{ + struct echo_TestSleep_private *p = te->private; + struct echo_TestSleep *r = p->r; + NTSTATUS status; + + r->out.result = r->in.seconds; + + status = dcesrv_reply(p->dce_call); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("echo_TestSleep_handler: dcesrv_reply() failed - %s\n", + nt_errstr(status))); + } +} + +static int echo_TestSleep_destructor(void *ptr) +{ + struct echo_TestSleep_private *p = ptr; + event_remove_timed(p->dce_call->event_ctx, p->te); + return 0; +} + static long echo_TestSleep(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSleep *r) { - sleep(r->in.seconds); - return r->in.seconds; + struct timed_event te; + struct echo_TestSleep_private *p; + + if (!(dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC)) { + /* we're not allowed to reply async */ + sleep(r->in.seconds); + return r->in.seconds; + } + + /* we're allowed to reply async */ + p = talloc(mem_ctx, struct echo_TestSleep_private); + if (!p) { + return 0; + } + + p->dce_call = dce_call; + p->r = r; + + te.handler = echo_TestSleep_handler; + te.private = p; + te.next_event = timeval_add(&dce_call->time, r->in.seconds, 0); + + p->te = event_add_timed(dce_call->event_ctx, &te); + if (!p->te) { + return 0; + } + + talloc_set_destructor(p, echo_TestSleep_destructor); + + dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; + return 0; } /* include the generated boilerplate */ diff --git a/source4/torture/rpc/echo.c b/source4/torture/rpc/echo.c index c547c71a83..13731383ee 100644 --- a/source4/torture/rpc/echo.c +++ b/source4/torture/rpc/echo.c @@ -3,6 +3,7 @@ test suite for echo rpc operations Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 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 @@ -208,7 +209,6 @@ static BOOL test_testcall2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) return ret; } -#if 0 /* test the TestSleep interface */ @@ -216,10 +216,13 @@ static BOOL test_sleep(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) { int i; NTSTATUS status; -#define ASYNC_COUNT 5 +#define ASYNC_COUNT 3 struct rpc_request *req[ASYNC_COUNT]; struct echo_TestSleep r[ASYNC_COUNT]; - int done[ASYNC_COUNT]; + BOOL done[ASYNC_COUNT]; + struct timeval snd[ASYNC_COUNT]; + struct timeval rcv[ASYNC_COUNT]; + struct timeval diff[ASYNC_COUNT]; struct event_context *ctx; int total_done = 0; BOOL ret = True; @@ -227,7 +230,9 @@ static BOOL test_sleep(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) printf("\nTesting TestSleep\n"); for (i=0;istate == RPC_REQUEST_DONE) { + if (done[i] == False && req[i]->state == RPC_REQUEST_DONE) { total_done++; - done[i] = 1; - status = dcerpc_ndr_request_recv(req[i]); + done[i] = True; + rcv[i] = timeval_current(); + diff[i] = timeval_diff(&rcv[i], &snd[i]); + status = dcerpc_ndr_request_recv(req[i]); if (!NT_STATUS_IS_OK(status)) { printf("TestSleep(%d) failed - %s\n", i, nt_errstr(status)); ret = False; + } else if (r[i].out.result != r[i].in.seconds) { + printf("Failed - Sleeped for %u seconds (but we said %u seconds and the reply takes only %u seconds)\n", + r[i].out.result, r[i].in.seconds, (uint_t)diff[i].tv_sec); + ret = False; } else { - printf("Sleep for %d seconds\n", - r[i].out.result); + if (r[i].out.result > diff[i].tv_sec) { + printf("Failed - Sleeped for %u seconds (but reply takes only %u seconds)\n", + r[i].out.result, (uint_t)diff[i].tv_sec); + ret = False; + } else if (r[i].out.result+1 == diff[i].tv_sec) { + printf("Sleeped for %u seconds (but reply takes %u seconds - busy server?)\n", + r[i].out.result, (uint_t)diff[i].tv_sec); + } else if (r[i].out.result == diff[i].tv_sec) { + printf("Sleeped for %u seconds (reply takes %u seconds - ok)\n", + r[i].out.result, (uint_t)diff[i].tv_sec); + } else { + printf("(Failed) - Not async - Sleeped for %u seconds (but reply takes %u seconds)\n", + r[i].out.result, (uint_t)diff[i].tv_sec); + /* TODO: let the test fail here, when we support async rpc on ncacn_np + ret = False;*/ + } } } } @@ -260,8 +285,6 @@ static BOOL test_sleep(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) return ret; } -#endif - /* test enum handling @@ -296,7 +319,6 @@ static BOOL test_enum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) return ret; } - BOOL torture_rpc_echo(void) { NTSTATUS status; @@ -342,11 +364,10 @@ BOOL torture_rpc_echo(void) ret = False; } -/* if (!test_sleep(p, mem_ctx)) { ret = False; } -*/ + printf("\n"); talloc_free(mem_ctx); -- cgit