Add the "SMBD" rpc transport
authorVolker Lendecke <vl@samba.org>
Thu, 29 Jan 2009 21:54:55 +0000 (22:54 +0100)
committerVolker Lendecke <vl@samba.org>
Fri, 30 Jan 2009 11:48:00 +0000 (12:48 +0100)
The idea of this is that all client utils like smbpasswd and also for example
"net join" do not access our internal databases like passdb and secrets.tdb
directly anymore but pass everything throught the well-established RPC
interfaces.

The way you use this is the following: With rpc_cli_smbd_conn_init() or its
async variant you initialize a "struct rpc_cli_smbd_conn". This structure is
the link to a freshly forked smbd, ready to be used for RPC services. You
should only ever have one such structure in your program. More don't hurt, but
are plainly unnecessary.

If you want to use the SAMR pipe to change a passwort, you connect to that pipe
with rpc_pipe_open_local. Do you normal rpccli_samr calls on that and your
locally forked smbd will connect to passdb for you.

GD, this might make the distinction between the _l and _r calls in libnetapi
mostly unnecessary. At least it is intended to do so... :-)

source3/Makefile.in
source3/include/proto.h
source3/include/smb.h
source3/rpc_client/cli_pipe.c
source3/rpc_client/rpc_transport_smbd.c [new file with mode: 0644]

index 5448643c1de0c129047762f125ef05fac7991393..9bc6329f82cf45521cf4b26ac11d542445b4542a 100644 (file)
@@ -579,7 +579,7 @@ RPC_PARSE_OBJ = $(RPC_PARSE_OBJ2) \
                rpc_parse/parse_eventlog.o rpc_parse/parse_buffer.o
 
 RPC_CLIENT_OBJ = rpc_client/cli_pipe.o rpc_client/rpc_transport_np.o \
-       rpc_client/rpc_transport_sock.o
+       rpc_client/rpc_transport_sock.o rpc_client/rpc_transport_smbd.o
 
 LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o
 
index 60e80bd87ddbd3645bcc123f6495d166980f74d3..d30eb09f6a52562e8df6d7f16934a8e33d182abd 100644 (file)
@@ -5326,6 +5326,10 @@ NTSTATUS cli_rpc_pipe_open_krb5(struct cli_state *cli,
 NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
                             struct rpc_pipe_client *cli,
                             DATA_BLOB *session_key);
+NTSTATUS rpc_pipe_open_local(TALLOC_CTX *mem_ctx,
+                            struct rpc_cli_smbd_conn *conn,
+                            const struct ndr_syntax_id *syntax,
+                            struct rpc_pipe_client **presult);
 
 /* The following definitions come from rpc_client/rpc_transport_np.c  */
 
@@ -5341,6 +5345,36 @@ NTSTATUS rpc_transport_np_init(TALLOC_CTX *mem_ctx, struct cli_state *cli,
                               struct rpc_cli_transport **presult);
 struct cli_state *rpc_pipe_np_smb_conn(struct rpc_pipe_client *p);
 
+/* The following definitions come from rpc_client/rpc_transport_smbd.c  */
+
+struct async_req *rpc_cli_smbd_conn_init_send(TALLOC_CTX *mem_ctx,
+                                             struct event_context *ev,
+                                             void (*stdout_callback)(char *buf,
+                                                                     size_t len,
+                                                                     void *priv),
+                                             void *priv);
+NTSTATUS rpc_cli_smbd_conn_init_recv(struct async_req *req,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct rpc_cli_smbd_conn **pconn);
+NTSTATUS rpc_cli_smbd_conn_init(TALLOC_CTX *mem_ctx,
+                               struct rpc_cli_smbd_conn **pconn,
+                               void (*stdout_callback)(char *buf,
+                                                       size_t len,
+                                                       void *priv),
+                               void *priv);
+
+struct async_req *rpc_transport_smbd_init_send(TALLOC_CTX *mem_ctx,
+                                              struct event_context *ev,
+                                              struct rpc_cli_smbd_conn *conn,
+                                              const struct ndr_syntax_id *abstract_syntax);
+NTSTATUS rpc_transport_smbd_init_recv(struct async_req *req,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct rpc_cli_transport **presult);
+NTSTATUS rpc_transport_smbd_init(TALLOC_CTX *mem_ctx,
+                                struct rpc_cli_smbd_conn *conn,
+                                const struct ndr_syntax_id *abstract_syntax,
+                                struct rpc_cli_transport **presult);
+
 /* The following definitions come from rpc_client/rpc_transport_sock.c  */
 
 NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd,
index 3f71cde3955c30e975e50bbdd3ff6f981698ac78..94517102559e4def50391f7c1724ac0f5924c307 100644 (file)
@@ -367,6 +367,7 @@ struct uuid;
 struct named_mutex;
 struct pcap_cache;
 struct wb_context;
+struct rpc_cli_smbd_conn;
 
 struct vfs_fsp_data {
     struct vfs_fsp_data *next;
index 5a53c0d94097180797d1272599b65c3aa90aa280..83247df46b187d16f2591da5eea7857ddb9e1bb7 100644 (file)
@@ -3511,6 +3511,61 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
        return NT_STATUS_OK;
 }
 
+NTSTATUS rpc_pipe_open_local(TALLOC_CTX *mem_ctx,
+                            struct rpc_cli_smbd_conn *conn,
+                            const struct ndr_syntax_id *syntax,
+                            struct rpc_pipe_client **presult)
+{
+       struct rpc_pipe_client *result;
+       struct cli_pipe_auth_data *auth;
+       NTSTATUS status;
+
+       result = talloc(mem_ctx, struct rpc_pipe_client);
+       if (result == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       result->abstract_syntax = *syntax;
+       result->transfer_syntax = ndr_transfer_syntax;
+       result->dispatch = cli_do_rpc_ndr;
+       result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+       result->max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
+
+       result->desthost = talloc_strdup(result, global_myname());
+       result->srv_name_slash = talloc_asprintf_strupper_m(
+               result, "\\\\%s", global_myname());
+       if ((result->desthost == NULL) || (result->srv_name_slash == NULL)) {
+               TALLOC_FREE(result);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = rpc_transport_smbd_init(result, conn, syntax,
+                                        &result->transport);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("rpc_transport_smbd_init failed: %s\n",
+                         nt_errstr(status)));
+               TALLOC_FREE(result);
+               return status;
+       }
+
+       status = rpccli_anon_bind_data(result, &auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("rpccli_anon_bind_data failed: %s\n",
+                         nt_errstr(status)));
+               TALLOC_FREE(result);
+               return status;
+       }
+
+       status = rpc_pipe_bind(result, auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("rpc_pipe_bind failed: %s\n", nt_errstr(status)));
+               TALLOC_FREE(result);
+               return status;
+       }
+
+       *presult = result;
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Open a pipe to a remote server.
  ****************************************************************************/
diff --git a/source3/rpc_client/rpc_transport_smbd.c b/source3/rpc_client/rpc_transport_smbd.c
new file mode 100644 (file)
index 0000000..0ff4ebd
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC client transport over named pipes to a child smbd
+ *  Copyright (C) Volker Lendecke 2009
+ *
+ *  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
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+/**
+ * struct rpc_cli_smbd_conn represents a forked smbd. This structure should
+ * exist only once per process which does the rpc calls.
+ *
+ * RPC pipe handles can be attached to this smbd connection with
+ * rpc_pipe_open_local().
+ *
+ * For this to work right, we can not use rpc_transport_np directly, because
+ * the child smbd wants to write its DEBUG output somewhere. We redirect the
+ * child's output to rpc_cli_smbd_conn->stdout_fd. While the RPC calls are
+ * active, we have an event context available and attach a fd event to the
+ * stdout_df.
+ */
+
+struct rpc_cli_smbd_conn {
+       /**
+        * The smb connection to handle the named pipe traffic over
+        */
+       struct cli_state *cli;
+
+       /**
+        * Attached to stdout in the forked smbd, this is where smbd will
+        * print its DEBUG.
+        */
+       int stdout_fd;
+
+       /**
+        * Custom callback provided by the owner of the
+        * rpc_cli_smbd_conn. Here we send the smbd DEBUG output. Can be NULL.
+        */
+       struct {
+               void (*fn)(char *buf, size_t len, void *priv);
+               void *priv;
+       } stdout_callback ;
+};
+
+/**
+ * Event handler to be called whenever the forked smbd prints debugging
+ * output.
+ */
+
+static void rpc_cli_smbd_stdout_reader(struct event_context *ev,
+                                      struct fd_event *fde,
+                                      uint16_t flags, void *priv)
+{
+       struct rpc_cli_smbd_conn *conn = talloc_get_type_abort(
+               priv, struct rpc_cli_smbd_conn);
+       char buf[1024];
+       ssize_t nread;
+
+       if ((flags & EVENT_FD_READ) == 0) {
+               return;
+       }
+
+       nread = read(conn->stdout_fd, buf, sizeof(buf)-1);
+       if (nread < 0) {
+               DEBUG(0, ("Could not read from smbd stdout: %s\n",
+                         strerror(errno)));
+               TALLOC_FREE(fde);
+               return;
+       }
+       if (nread == 0) {
+               DEBUG(0, ("EOF from smbd stdout\n"));
+               TALLOC_FREE(fde);
+               return;
+       }
+
+       if (conn->stdout_callback.fn != NULL) {
+               conn->stdout_callback.fn(buf, nread,
+                                        conn->stdout_callback.priv);
+       }
+}
+
+/**
+ * struct rpc_transport_smbd_state is the link from a struct rpc_pipe_client
+ * to the rpc_cli_smbd_conn. We use a named pipe transport as a subtransport.
+ */
+
+struct rpc_transport_smbd_state {
+       struct rpc_cli_smbd_conn *conn;
+       struct rpc_cli_transport *sub_transp;
+};
+
+static int rpc_cli_smbd_conn_destructor(struct rpc_cli_smbd_conn *conn)
+{
+       if (conn->cli != NULL) {
+               cli_shutdown(conn->cli);
+               conn->cli = NULL;
+       }
+       if (conn->stdout_fd != -1) {
+               close(conn->stdout_fd);
+               conn->stdout_fd = -1;
+       }
+       return 0;
+}
+
+/*
+ * Do the negprot/sesssetup/tcon to an anonymous ipc$ connection
+ */
+
+struct get_anon_ipc_state {
+       struct event_context *ev;
+       struct cli_state *cli;
+};
+
+static void get_anon_ipc_negprot_done(struct async_req *subreq);
+static void get_anon_ipc_sesssetup_done(struct async_req *subreq);
+static void get_anon_ipc_tcon_done(struct async_req *subreq);
+
+static struct async_req *get_anon_ipc_send(TALLOC_CTX *mem_ctx,
+                                          struct event_context *ev,
+                                          struct cli_state *cli)
+{
+       struct async_req *result, *subreq;
+       struct get_anon_ipc_state *state;
+
+       if (!async_req_setup(mem_ctx, &result, &state,
+                            struct get_anon_ipc_state)) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->cli = cli;
+
+       subreq = cli_negprot_send(state, ev, cli);
+       if (subreq == NULL) {
+               goto fail;
+       }
+       subreq->async.fn = get_anon_ipc_negprot_done;
+       subreq->async.priv = result;
+       return result;
+ fail:
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static void get_anon_ipc_negprot_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       struct get_anon_ipc_state *state = talloc_get_type_abort(
+               req->private_data, struct get_anon_ipc_state);
+       NTSTATUS status;
+
+       status = cli_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+
+       subreq = cli_session_setup_guest_send(state, state->ev, state->cli);
+       if (async_req_nomem(subreq, req)) {
+               return;
+       }
+       subreq->async.fn = get_anon_ipc_sesssetup_done;
+       subreq->async.priv = req;
+}
+
+static void get_anon_ipc_sesssetup_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       struct get_anon_ipc_state *state = talloc_get_type_abort(
+               req->private_data, struct get_anon_ipc_state);
+       NTSTATUS status;
+
+       status = cli_session_setup_guest_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+
+       subreq = cli_tcon_andx_send(state, state->ev, state->cli,
+                                   "IPC$", "IPC", NULL, 0);
+       if (async_req_nomem(subreq, req)) {
+               return;
+       }
+       subreq->async.fn = get_anon_ipc_tcon_done;
+       subreq->async.priv = req;
+}
+
+static void get_anon_ipc_tcon_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       NTSTATUS status;
+
+       status = cli_tcon_andx_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+       async_req_done(req);
+}
+
+static NTSTATUS get_anon_ipc_recv(struct async_req *req)
+{
+       return async_req_simple_recv(req);
+}
+
+struct rpc_cli_smbd_conn_init_state {
+       struct event_context *ev;
+       struct rpc_cli_smbd_conn *conn;
+};
+
+static void rpc_cli_smbd_conn_init_done(struct async_req *subreq);
+
+struct async_req *rpc_cli_smbd_conn_init_send(TALLOC_CTX *mem_ctx,
+                                             struct event_context *ev,
+                                             void (*stdout_callback)(char *buf,
+                                                                     size_t len,
+                                                                     void *priv),
+                                             void *priv)
+{
+       struct async_req *result, *subreq;
+       struct rpc_cli_smbd_conn_init_state *state;
+       int smb_sock[2];
+       int stdout_pipe[2];
+       NTSTATUS status;
+       pid_t pid;
+       int ret;
+
+       smb_sock[0] = smb_sock[1] = stdout_pipe[0] = stdout_pipe[1] = -1;
+
+       if (!async_req_setup(mem_ctx, &result, &state,
+                            struct rpc_cli_smbd_conn_init_state)) {
+               return NULL;
+       }
+       state->ev = ev;
+
+       state->conn = talloc(state, struct rpc_cli_smbd_conn);
+       if (state->conn == NULL) {
+               goto nomem;
+       }
+
+       state->conn->cli = cli_initialise();
+       if (state->conn->cli == NULL) {
+               goto nomem;
+       }
+       state->conn->stdout_fd = -1;
+       state->conn->stdout_callback.fn = stdout_callback;
+       state->conn->stdout_callback.priv = priv;
+       talloc_set_destructor(state->conn, rpc_cli_smbd_conn_destructor);
+
+       ret = socketpair(AF_UNIX, SOCK_STREAM, 0, smb_sock);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto post_status;
+       }
+       ret = pipe(stdout_pipe);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto post_status;
+       }
+
+       pid = sys_fork();
+       if (pid == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto post_status;
+       }
+       if (pid == 0) {
+               char *smbd_cmd;
+
+               close(smb_sock[0]);
+               close(stdout_pipe[0]);
+               close(0);
+               if (dup(smb_sock[1]) == -1) {
+                       exit(1);
+               }
+               close(smb_sock[1]);
+               close(1);
+               if (dup(stdout_pipe[1]) == -1) {
+                       exit(1);
+               }
+               close(stdout_pipe[1]);
+
+               smbd_cmd = getenv("SMB_PATH");
+
+               if ((smbd_cmd == NULL)
+                   && (asprintf(&smbd_cmd, "%s/smbd", get_dyn_SBINDIR())
+                       == -1)) {
+                       printf("no memory");
+                       exit(1);
+               }
+               if (asprintf(&smbd_cmd, "%s -F -S", smbd_cmd) == -1) {
+                       printf("no memory");
+                       exit(1);
+               }
+
+               exit(system(smbd_cmd));
+       }
+
+       state->conn->cli->fd = smb_sock[0];
+       smb_sock[0] = -1;
+       close(smb_sock[1]);
+       smb_sock[1] = -1;
+
+       state->conn->stdout_fd = stdout_pipe[0];
+       stdout_pipe[0] = -1;
+       close(stdout_pipe[1]);
+       stdout_pipe[1] = -1;
+
+       subreq = get_anon_ipc_send(state, ev, state->conn->cli);
+       if (subreq == NULL) {
+               goto nomem;
+       }
+
+       if (event_add_fd(ev, subreq, state->conn->stdout_fd, EVENT_FD_READ,
+                        rpc_cli_smbd_stdout_reader, state->conn) == NULL) {
+               goto nomem;
+       }
+
+       subreq->async.fn = rpc_cli_smbd_conn_init_done;
+       subreq->async.priv = result;
+       return result;
+
+ nomem:
+       status = NT_STATUS_NO_MEMORY;
+ post_status:
+       if (smb_sock[0] != -1) {
+               close(smb_sock[0]);
+       }
+       if (smb_sock[1] != -1) {
+               close(smb_sock[1]);
+       }
+       if (stdout_pipe[0] != -1) {
+               close(stdout_pipe[0]);
+       }
+       if (stdout_pipe[1] != -1) {
+               close(stdout_pipe[1]);
+       }
+       if (async_post_status(result, ev, status)) {
+               return result;
+       }
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static void rpc_cli_smbd_conn_init_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       NTSTATUS status;
+
+       status = get_anon_ipc_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+       async_req_done(req);
+}
+
+NTSTATUS rpc_cli_smbd_conn_init_recv(struct async_req *req,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct rpc_cli_smbd_conn **pconn)
+{
+       struct rpc_cli_smbd_conn_init_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_cli_smbd_conn_init_state);
+       NTSTATUS status;
+
+       if (async_req_is_error(req, &status)) {
+               return status;
+       }
+       *pconn = talloc_move(mem_ctx, &state->conn);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_cli_smbd_conn_init(TALLOC_CTX *mem_ctx,
+                               struct rpc_cli_smbd_conn **pconn,
+                               void (*stdout_callback)(char *buf,
+                                                       size_t len,
+                                                       void *priv),
+                               void *priv)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct async_req *req;
+       NTSTATUS status;
+
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       req = rpc_cli_smbd_conn_init_send(frame, ev, stdout_callback, priv);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       while (req->state < ASYNC_REQ_DONE) {
+               event_loop_once(ev);
+       }
+
+       status = rpc_cli_smbd_conn_init_recv(req, mem_ctx, pconn);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct rpc_smbd_write_state {
+       struct rpc_cli_transport *sub_transp;
+       ssize_t written;
+};
+
+static void rpc_smbd_write_done(struct async_req *subreq);
+
+static struct async_req *rpc_smbd_write_send(TALLOC_CTX *mem_ctx,
+                                            struct event_context *ev,
+                                            const uint8_t *data, size_t size,
+                                            void *priv)
+{
+       struct rpc_transport_smbd_state *transp = talloc_get_type_abort(
+               priv, struct rpc_transport_smbd_state);
+       struct async_req *result, *subreq;
+       struct rpc_smbd_write_state *state;
+
+       if (!async_req_setup(mem_ctx, &result, &state,
+                            struct rpc_smbd_write_state)) {
+               return NULL;
+       }
+       state->sub_transp = transp->sub_transp;
+
+       subreq = transp->sub_transp->write_send(state, ev, data, size,
+                                               transp->sub_transp->priv);
+       if (subreq == NULL) {
+               goto fail;
+       }
+
+       if (event_add_fd(ev, subreq, transp->conn->stdout_fd, EVENT_FD_READ,
+                        rpc_cli_smbd_stdout_reader, transp->conn) == NULL) {
+               goto fail;
+       }
+
+       subreq->async.fn = rpc_smbd_write_done;
+       subreq->async.priv = result;
+       return result;
+
+ fail:
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static void rpc_smbd_write_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       struct rpc_smbd_write_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_smbd_write_state);
+       NTSTATUS status;
+
+       status = state->sub_transp->write_recv(subreq, &state->written);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+       async_req_done(req);
+}
+
+static NTSTATUS rpc_smbd_write_recv(struct async_req *req, ssize_t *pwritten)
+{
+       struct rpc_smbd_write_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_smbd_write_state);
+       NTSTATUS status;
+
+       if (async_req_is_error(req, &status)) {
+               return status;
+       }
+       *pwritten = state->written;
+       return NT_STATUS_OK;
+}
+
+struct rpc_smbd_read_state {
+       struct rpc_cli_transport *sub_transp;
+       ssize_t received;
+};
+
+static void rpc_smbd_read_done(struct async_req *subreq);
+
+static struct async_req *rpc_smbd_read_send(TALLOC_CTX *mem_ctx,
+                                           struct event_context *ev,
+                                           uint8_t *data, size_t size,
+                                           void *priv)
+{
+       struct rpc_transport_smbd_state *transp = talloc_get_type_abort(
+               priv, struct rpc_transport_smbd_state);
+       struct async_req *result, *subreq;
+       struct rpc_smbd_read_state *state;
+
+       if (!async_req_setup(mem_ctx, &result, &state,
+                            struct rpc_smbd_read_state)) {
+               return NULL;
+       }
+       state->sub_transp = transp->sub_transp;
+
+       subreq = transp->sub_transp->read_send(state, ev, data, size,
+                                               transp->sub_transp->priv);
+       if (subreq == NULL) {
+               goto fail;
+       }
+
+       if (event_add_fd(ev, subreq, transp->conn->stdout_fd, EVENT_FD_READ,
+                        rpc_cli_smbd_stdout_reader, transp->conn) == NULL) {
+               goto fail;
+       }
+
+       subreq->async.fn = rpc_smbd_read_done;
+       subreq->async.priv = result;
+       return result;
+
+ fail:
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static void rpc_smbd_read_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       struct rpc_smbd_read_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_smbd_read_state);
+       NTSTATUS status;
+
+       status = state->sub_transp->read_recv(subreq, &state->received);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+       async_req_done(req);
+}
+
+static NTSTATUS rpc_smbd_read_recv(struct async_req *req, ssize_t *preceived)
+{
+       struct rpc_smbd_read_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_smbd_read_state);
+       NTSTATUS status;
+
+       if (async_req_is_error(req, &status)) {
+               return status;
+       }
+       *preceived = state->received;
+       return NT_STATUS_OK;
+}
+
+struct rpc_transport_smbd_init_state {
+       struct rpc_cli_transport *transport;
+       struct rpc_transport_smbd_state *transport_smbd;
+};
+
+static void rpc_transport_smbd_init_done(struct async_req *subreq);
+
+struct async_req *rpc_transport_smbd_init_send(TALLOC_CTX *mem_ctx,
+                                              struct event_context *ev,
+                                              struct rpc_cli_smbd_conn *conn,
+                                              const struct ndr_syntax_id *abstract_syntax)
+{
+       struct async_req *result, *subreq;
+       struct rpc_transport_smbd_init_state *state;
+
+       if (!async_req_setup(mem_ctx, &result, &state,
+                            struct rpc_transport_smbd_init_state)) {
+               return NULL;
+       }
+
+       state->transport = talloc(state, struct rpc_cli_transport);
+       if (state->transport == NULL) {
+               goto fail;
+       }
+       state->transport_smbd = talloc(state->transport,
+                                      struct rpc_transport_smbd_state);
+       if (state->transport_smbd == NULL) {
+               goto fail;
+       }
+       state->transport_smbd->conn = conn;
+       state->transport->priv = state->transport_smbd;
+
+       subreq = rpc_transport_np_init_send(state, ev, conn->cli,
+                                           abstract_syntax);
+       if (subreq == NULL) {
+               goto fail;
+       }
+       subreq->async.fn = rpc_transport_smbd_init_done;
+       subreq->async.priv = result;
+       return result;
+
+ fail:
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static void rpc_transport_smbd_init_done(struct async_req *subreq)
+{
+       struct async_req *req = talloc_get_type_abort(
+               subreq->async.priv, struct async_req);
+       struct rpc_transport_smbd_init_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_transport_smbd_init_state);
+       NTSTATUS status;
+
+       status = rpc_transport_np_init_recv(
+               subreq, state->transport_smbd,
+               &state->transport_smbd->sub_transp);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               async_req_error(req, status);
+               return;
+       }
+       async_req_done(req);
+}
+
+NTSTATUS rpc_transport_smbd_init_recv(struct async_req *req,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct rpc_cli_transport **presult)
+{
+       struct rpc_transport_smbd_init_state *state = talloc_get_type_abort(
+               req->private_data, struct rpc_transport_smbd_init_state);
+       NTSTATUS status;
+
+       if (async_req_is_error(req, &status)) {
+               return status;
+       }
+
+       state->transport->write_send = rpc_smbd_write_send;
+       state->transport->write_recv = rpc_smbd_write_recv;
+       state->transport->read_send = rpc_smbd_read_send;
+       state->transport->read_recv = rpc_smbd_read_recv;
+       state->transport->trans_send = NULL;
+       state->transport->trans_recv = NULL;
+
+       *presult = talloc_move(mem_ctx, &state->transport);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_transport_smbd_init(TALLOC_CTX *mem_ctx,
+                                struct rpc_cli_smbd_conn *conn,
+                                const struct ndr_syntax_id *abstract_syntax,
+                                struct rpc_cli_transport **presult)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct async_req *req;
+       NTSTATUS status;
+
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       req = rpc_transport_smbd_init_send(frame, ev, conn, abstract_syntax);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       while (req->state < ASYNC_REQ_DONE) {
+               event_loop_once(ev);
+       }
+
+       status = rpc_transport_smbd_init_recv(req, mem_ctx, presult);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}