From ebe6627c1f0e6b488a0c456860a055fd5701e84d Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Mon, 3 Mar 2014 19:49:35 +0100 Subject: [PATCH] rpc_client: retry open on STATUS_PIPE_NOT_AVAILABLE 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 Reviewed-by: Jeremy Allison --- 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 86190c67338..7f2f1bf5162 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; } -- 2.11.4.GIT