r4690: - add support for async rpc server replies
authorStefan Metzmacher <metze@samba.org>
Tue, 11 Jan 2005 16:53:02 +0000 (16:53 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:08:43 +0000 (13:08 -0500)
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

source/build/pidl/server.pm
source/build/pidl/stub.pm
source/ntvfs/ipc/vfs_ipc.c
source/rpc_server/dcerpc_server.c
source/rpc_server/dcerpc_server.h
source/rpc_server/dcerpc_sock.c
source/rpc_server/echo/rpc_echo.c
source/torture/rpc/echo.c

index 5c70adf2af138afc16ec5835df2791123abbaa67..e080bf56547011606bf53c4ff42221b5d34681f1 100644 (file)
@@ -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;
-
index d1448d467be6e81906f691a719575ac216d8884f..a9c938df63b1ffe9731d49a66f7d574abb1618ce 100644 (file)
@@ -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 = "";
index 2a19de1ec00fa1220559f2e40c9df822eefe91ac..402d1ead64cff7a7783d260cb997b82ba6a12fe2 100644 (file)
@@ -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);
index 925004b191ab06ee303e4daa41c9e5ed2f589360..f031bcb50b05282cb9cf417d919f500c9099dc0a 100644 (file)
@@ -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)
index 49fbd4477b2ded611aa7f58f6bde57e23bdf52d4..2738cee0403314efef4021127a26793716c613af 100644 (file)
@@ -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 */
index 2155f9737610ded2db623d13c9cafaf1624d9fca..420c73cb1c0df76e9d450b67d84d9574df9183e2 100644 (file)
@@ -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; 
 }
index e5c1ee56cee606a45eef1cac36c79935b43f1a81..164e2d588f8712a529a6bd1467af3408cebfa50f 100644 (file)
@@ -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 */
index c547c71a83189732796ef59055940567090947ad..13731383eee4888a276cd3ae564bc293c9ccfbb0 100644 (file)
@@ -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;i<ASYNC_COUNT;i++) {
-               done[i] = 0;
+               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, mem_ctx, &r[i]);
                if (!req[i]) {
@@ -242,17 +247,37 @@ static BOOL test_sleep(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
                        return False;
                }
                for (i=0;i<ASYNC_COUNT;i++) {
-                       if (done[i] == 0 && req[i]->state == 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);