/*
 *  Unix SMB/CIFS implementation.
 *  RPC Pipe client / server routines
 *  Copyright (C) Andrew Tridgell              1992-1998,
 *  Largely re-written : 2005
 *  Copyright (C) Jeremy Allison		1998 - 2005
 *  Copyright (C) Simo Sorce			2010
 *  Copyright (C) Andrew Bartlett		2011
 *
 *  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"
#include "rpc_client/cli_pipe.h"
#include "rpc_dce.h"
#include "../libcli/named_pipe_auth/npa_tstream.h"
#include "rpc_server/rpc_ncacn_np.h"
#include "librpc/gen_ndr/netlogon.h"
#include "librpc/gen_ndr/auth.h"
#include "../auth/auth_sam_reply.h"
#include "../auth/auth_util.h"
#include "auth.h"
#include "rpc_server/rpc_pipes.h"
#include "../lib/tsocket/tsocket.h"
#include "../lib/util/tevent_ntstatus.h"
#include "rpc_server/rpc_config.h"
#include "librpc/ndr/ndr_table.h"
#include "rpc_server/rpc_server.h"
#include "librpc/rpc/dcerpc_util.h"

#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV

struct np_proxy_state {
	uint16_t file_type;
	uint16_t device_state;
	uint64_t allocation_size;
	struct tstream_context *npipe;
	struct tevent_queue *read_queue;
	struct tevent_queue *write_queue;
};

struct npa_state *npa_state_init(TALLOC_CTX *mem_ctx)
{
	struct npa_state *npa;

	npa = talloc_zero(mem_ctx, struct npa_state);
	if (npa == NULL) {
		return NULL;
	}

	npa->read_queue = tevent_queue_create(npa, "npa_cli_read");
	if (npa->read_queue == NULL) {
		DEBUG(0, ("tevent_queue_create failed\n"));
		goto fail;
	}

	npa->write_queue = tevent_queue_create(npa, "npa_cli_write");
	if (npa->write_queue == NULL) {
		DEBUG(0, ("tevent_queue_create failed\n"));
		goto fail;
	}

	return npa;
fail:
	talloc_free(npa);
	return NULL;
}

/**
 * @brief Create a new DCERPC Binding Handle which uses a local dispatch function.
 *
 * @param[in]  mem_ctx  The memory context to use.
 *
 * @param[in]  ndr_table Normally the ndr_table_<name>.
 *
 * @param[in]  remote_address The info about the connected client.
 *
 * @param[in]  serversupplied_info The server supplied authentication function.
 *
 * @param[in]  msg_ctx   The messaging context that can be used by the server
 *
 * @param[out] binding_handle  A pointer to store the connected
 *                             dcerpc_binding_handle
 *
 * @return              NT_STATUS_OK on success, a corresponding NT status if an
 *                      error occurred.
 *
 * @code
 *   struct dcerpc_binding_handle *winreg_binding;
 *   NTSTATUS status;
 *
 *   status = rpcint_binding_handle(tmp_ctx,
 *                                  &ndr_table_winreg,
 *                                  p->remote_address,
 *                                  p->session_info,
 *                                  p->msg_ctx
 *                                  &winreg_binding);
 * @endcode
 */
NTSTATUS rpcint_binding_handle(TALLOC_CTX *mem_ctx,
			       const struct ndr_interface_table *ndr_table,
			       const struct tsocket_address *remote_address,
			       const struct tsocket_address *local_address,
			       const struct auth_session_info *session_info,
			       struct messaging_context *msg_ctx,
			       struct dcerpc_binding_handle **binding_handle)
{
	struct rpc_pipe_client *rpccli = NULL;
	NTSTATUS status;

	status = rpc_pipe_open_local_np(
		mem_ctx,
		ndr_table,
		NULL,
		remote_address,
		NULL,
		local_address,
		session_info,
		&rpccli);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_DEBUG("rpc_pipe_open_local_np failed: %s\n",
			  nt_errstr(status));
		goto fail;
	}

	*binding_handle = rpccli->binding_handle;
	return NT_STATUS_OK;
fail:
	TALLOC_FREE(rpccli);
	return status;
}

/**
 * @brief Create a new RPC client context which uses a local dispatch function
 *	  or a remote transport, depending on rpc_server configuration for the
 *	  specific service.
 *
 * @param[in]  mem_ctx  The memory context to use.
 *
 * @param[in]  abstract_syntax Normally the syntax_id of the autogenerated
 *                             ndr_table_<name>.
 *
 * @param[in]  serversupplied_info The server supplied authentication function.
 *
 * @param[in]  remote_address The client address information.
 *
 * @param[in]  msg_ctx  The messaging context to use.
 *
 * @param[out] presult  A pointer to store the connected rpc client pipe.
 *
 * @return              NT_STATUS_OK on success, a corresponding NT status if an
 *                      error occurred.
 *
 * @code
 *   struct rpc_pipe_client *winreg_pipe;
 *   NTSTATUS status;
 *
 *   status = rpc_pipe_open_interface(tmp_ctx,
 *                                    &ndr_table_winreg.syntax_id,
 *                                    p->session_info,
 *                                    remote_address,
 *                                    &winreg_pipe);
 * @endcode
 */

NTSTATUS rpc_pipe_open_interface(TALLOC_CTX *mem_ctx,
				 const struct ndr_interface_table *table,
				 const struct auth_session_info *session_info,
				 const struct tsocket_address *remote_address,
				 const struct tsocket_address *local_address,
				 struct messaging_context *msg_ctx,
				 struct rpc_pipe_client **cli_pipe)
{
	struct rpc_pipe_client *cli = NULL;
	NTSTATUS status;

	if (cli_pipe != NULL) {
		if (rpccli_is_connected(*cli_pipe)) {
			return NT_STATUS_OK;
		} else {
			TALLOC_FREE(*cli_pipe);
		}
	}

	status = rpc_pipe_open_local_np(
		mem_ctx,
		table,
		NULL,
		remote_address,
		NULL,
		local_address,
		session_info,
		&cli);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_ERR("Could not connect to %s pipe: %s\n",
			table->name,
			nt_errstr(status));
		return status;
	}

	if (NT_STATUS_IS_OK(status) && cli_pipe != NULL) {
		*cli_pipe = cli;
	}
	return status;
}
