/* * Unix SMB/CIFS implementation. * RPC client transport over named pipes * 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 . */ #include "includes.h" #include "../lib/util/tevent_ntstatus.h" #include "librpc/rpc/dcerpc_util.h" #include "rpc_client/rpc_transport.h" #include "librpc/ndr/ndr_table.h" #include "libcli/smb/smbXcli_base.h" #include "libcli/smb/tstream_smbXcli_np.h" #include "client.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_CLI 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); struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, const struct ndr_interface_table *table) { struct tevent_req *req; struct rpc_transport_np_init_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct rpc_transport_np_init_state); if (req == NULL) { return NULL; } if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { state->tcon = cli->smb2.tcon; state->session = cli->smb2.session; } else { state->tcon = cli->smb1.tcon; state->session = cli->smb1.session; state->pid = cli->smb1.pid; } 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 (state->pipe_name[0] == '\\') { state->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); } tevent_req_set_callback(subreq, rpc_transport_np_init_pipe_open, req); 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( subreq, struct tevent_req); struct rpc_transport_np_init_state *state = tevent_req_data( req, struct rpc_transport_np_init_state); NTSTATUS status; struct tstream_context *stream; status = tstream_smbXcli_np_open_recv(subreq, state, &stream); TALLOC_FREE(subreq); 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)) { DEBUG(2, ("Failed to create asynchronous " "tevent_timer")); } return; } if (tevent_req_nterror(req, status)) { return; } status = rpc_transport_tstream_init(state, &stream, &state->transport); if (tevent_req_nterror(req, status)) { return; } tevent_req_done(req); } NTSTATUS rpc_transport_np_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct rpc_cli_transport **presult) { struct rpc_transport_np_init_state *state = tevent_req_data( req, struct rpc_transport_np_init_state); NTSTATUS status; if (tevent_req_is_nterror(req, &status)) { return status; } *presult = talloc_move(mem_ctx, &state->transport); return NT_STATUS_OK; }