[PATCH] rpc_client: retry open on STATUS_PIPE_NOT_AVAILABLE

David Disseldorp ddiss at samba.org
Mon Mar 3 11:49:35 MST 2014


Windows Server starts some named pipe services on demand, and responds
to initial open requests with STATUS_PIPE_NOT_AVAILABLE. The FssagentRpc
named pipe on Windows Server 2012 exhibits this behaviour.

This change sees rpcclient retry named pipe open requests when the
server responds with STATUS_PIPE_NOT_AVAILABLE. The retry logic is
contained in an asynchronous tevent_timer callback, to allow for
non-blocking callers.

Signed-off-by: David Disseldorp <ddiss at samba.org>
---
 source3/rpc_client/rpc_transport_np.c | 85 ++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 17 deletions(-)

diff --git a/source3/rpc_client/rpc_transport_np.c b/source3/rpc_client/rpc_transport_np.c
index 86190c6..7f2f1bf 100644
--- a/source3/rpc_client/rpc_transport_np.c
+++ b/source3/rpc_client/rpc_transport_np.c
@@ -30,6 +30,15 @@
 
 struct rpc_transport_np_init_state {
 	struct rpc_cli_transport *transport;
+	int retries;
+	struct tevent_context *ev;
+	struct smbXcli_conn *conn;
+	int timeout;
+	struct timeval abs_timeout;
+	const char *pipe_name;
+	struct smbXcli_session *session;
+	struct smbXcli_tcon *tcon;
+	uint16_t pid;
 };
 
 static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq);
@@ -41,11 +50,7 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
 {
 	struct tevent_req *req;
 	struct rpc_transport_np_init_state *state;
-	const char *pipe_name;
 	struct tevent_req *subreq;
-	struct smbXcli_session *session;
-	struct smbXcli_tcon *tcon;
-	uint16_t pid = 0;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct rpc_transport_np_init_state);
@@ -54,26 +59,32 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		tcon = cli->smb2.tcon;
-		session = cli->smb2.session;
+		state->tcon = cli->smb2.tcon;
+		state->session = cli->smb2.session;
 	} else {
-		tcon = cli->smb1.tcon;
-		session = cli->smb1.session;
-		pid = cli->smb1.pid;
+		state->tcon = cli->smb1.tcon;
+		state->session = cli->smb1.session;
+		state->pid = cli->smb1.pid;
 	}
 
-	pipe_name = dcerpc_default_transport_endpoint(mem_ctx, NCACN_NP, table);
-	if (tevent_req_nomem(pipe_name, req)) {
+	state->ev = ev;
+	state->conn = cli->conn;
+	state->timeout = cli->timeout;
+	state->abs_timeout = timeval_current_ofs_msec(cli->timeout);
+	state->pipe_name = dcerpc_default_transport_endpoint(state, NCACN_NP,
+							     table);
+	if (tevent_req_nomem(state->pipe_name, req)) {
 		return tevent_req_post(req, ev);
 	}
 
-	while (pipe_name[0] == '\\') {
-		pipe_name++;
+	while (state->pipe_name[0] == '\\') {
+		state->pipe_name++;
 	}
 
-	subreq = tstream_smbXcli_np_open_send(state, ev, cli->conn,
-					      session, tcon, pid,
-					      cli->timeout, pipe_name);
+	subreq = tstream_smbXcli_np_open_send(state, ev, state->conn,
+					      state->session, state->tcon,
+					      state->pid, state->timeout,
+					      state->pipe_name);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
@@ -82,6 +93,30 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
 	return req;
 }
 
+static void rpc_transport_np_init_pipe_open_retry(struct tevent_context *ev,
+						  struct tevent_timer *te,
+						  struct timeval t,
+						  void *priv_data)
+{
+	struct tevent_req *subreq;
+	struct tevent_req *req = talloc_get_type(priv_data, struct tevent_req);
+	struct rpc_transport_np_init_state *state = tevent_req_data(
+		req, struct rpc_transport_np_init_state);
+
+	subreq = tstream_smbXcli_np_open_send(state, ev,
+					      state->conn,
+					      state->session,
+					      state->tcon,
+					      state->pid,
+					      state->timeout,
+					      state->pipe_name);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, rpc_transport_np_init_pipe_open, req);
+	state->retries++;
+}
+
 static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
@@ -93,7 +128,23 @@ static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq)
 
 	status = tstream_smbXcli_np_open_recv(subreq, state, &stream);
 	TALLOC_FREE(subreq);
-	if (!NT_STATUS_IS_OK(status)) {
+	if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_NOT_AVAILABLE)
+				&& (!timeval_expired(&state->abs_timeout))) {
+		struct tevent_timer *te;
+		/*
+		 * Retry on STATUS_PIPE_NOT_AVAILABLE, Windows starts some
+		 * servers (FssagentRpc) on demand.
+		 */
+		DEBUG(2, ("RPC pipe %s not available, retry %d\n",
+			  state->pipe_name, state->retries));
+		te = tevent_add_timer(state->ev, state,
+				 timeval_current_ofs_msec(100 * state->retries),
+				 rpc_transport_np_init_pipe_open_retry, req);
+		if (tevent_req_nomem(te, req)) {
+			return;
+		}
+		return;
+	} else if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(req, status);
 		return;
 	}
-- 
1.8.4.5



More information about the samba-technical mailing list