#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/poll.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <netsmb/smbfs_api.h>
#include <smb/ntstatus.h>
#include <libmlrpc.h>
#include <assert.h>
static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
static void ndr_xa_release(ndr_client_t *);
int rpc_pipe_open_retries = 10;
int
mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx)
{
ndr_client_t *clnt = NULL;
if (ctx == NULL)
return (EINVAL);
if ((clnt = calloc(1, sizeof (*clnt))) == NULL)
return (ENOMEM);
clnt->xa_fd = -1;
clnt->xa_private = ctx;
clnt->xa_init = ndr_xa_init;
clnt->xa_exchange = ndr_xa_exchange;
clnt->xa_read = ndr_xa_read;
clnt->xa_preserve = ndr_xa_preserve;
clnt->xa_destruct = ndr_xa_destruct;
clnt->xa_release = ndr_xa_release;
clnt->handle = &handle->handle;
ndr_svc_binding_pool_init(&clnt->binding_list,
clnt->binding_pool, NDR_N_BINDING_POOL);
if ((clnt->heap = ndr_heap_create()) == NULL)
goto nomem;
bzero(handle, sizeof (*handle));
handle->clnt = clnt;
return (0);
nomem:
free(clnt);
return (ENOMEM);
}
uint32_t
mlrpc_clh_set_auth(mlrpc_handle_t *handle, ndr_auth_ctx_t *auth_ctx)
{
ndr_client_t *clnt = NULL;
if ((clnt = handle->clnt) == NULL)
return (NT_STATUS_INTERNAL_ERROR);
if (auth_ctx != NULL) {
clnt->auth_ctx = *auth_ctx;
}
return (NT_STATUS_SUCCESS);
}
uint32_t
mlrpc_clh_bind(mlrpc_handle_t *handle, ndr_service_t *svc)
{
ndr_client_t *clnt = NULL;
struct smb_ctx *ctx = NULL;
uint32_t status = 0;
int fd = -1;
int rc, retries;
if ((clnt = handle->clnt) == NULL)
return (NT_STATUS_INTERNAL_ERROR);
if ((ctx = clnt->xa_private) == NULL)
return (NT_STATUS_INTERNAL_ERROR);
if (clnt->xa_fd != -1)
return (NT_STATUS_INTERNAL_ERROR);
retries = rpc_pipe_open_retries;
retry_open:
fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
if (fd < 0) {
rc = errno;
switch (rc) {
case EBUSY:
if (--retries > 0) {
(void) poll(NULL, 0, 500);
goto retry_open;
}
status = RPC_NT_SERVER_TOO_BUSY;
break;
case EACCES:
status = NT_STATUS_ACCESS_DENIED;
break;
default:
status = RPC_NT_SERVER_UNAVAILABLE;
break;
}
return (status);
}
clnt->xa_fd = fd;
bzero(&handle->handle, sizeof (ndr_hdid_t));
rc = ndr_clnt_bind(clnt, svc, &clnt->binding);
switch (rc) {
case NDR_DRC_FAULT_OUT_OF_MEMORY:
status = NT_STATUS_NO_MEMORY;
break;
case NDR_DRC_FAULT_API_SERVICE_INVALID:
status = NT_STATUS_INTERNAL_ERROR;
break;
default:
if (NDR_DRC_IS_FAULT(rc)) {
status = RPC_NT_PROTOCOL_ERROR;
break;
}
case NDR_DRC_OK:
status = NT_STATUS_SUCCESS;
}
if (status != 0) {
if (fd != -1)
(void) smb_fh_close(fd);
clnt->xa_fd = -1;
}
return (status);
}
void
mlrpc_clh_unbind(mlrpc_handle_t *handle)
{
ndr_client_t *clnt = handle->clnt;
if (clnt->xa_fd != -1) {
(void) smb_fh_close(clnt->xa_fd);
clnt->xa_fd = -1;
}
}
void *
mlrpc_clh_free(mlrpc_handle_t *handle)
{
ndr_client_t *clnt = handle->clnt;
void *private;
if (clnt == NULL)
return (NULL);
assert(handle->clnt->handle == &handle->handle);
mlrpc_clh_unbind(handle);
if (clnt->heap_preserved)
ndr_clnt_free_heap(clnt);
else
ndr_heap_destroy(clnt->heap);
private = clnt->xa_private;
free(clnt);
bzero(handle, sizeof (*handle));
return (private);
}
int
ndr_rpc_call(mlrpc_handle_t *handle, int opnum, void *params)
{
ndr_client_t *clnt = handle->clnt;
int rc;
if (ndr_rpc_get_heap(handle) == NULL)
return (-1);
rc = ndr_clnt_call(clnt->binding, opnum, params);
clnt->nonull = B_FALSE;
if (NDR_DRC_IS_FAULT(rc)) {
ndr_rpc_release(handle);
return (-1);
}
return (0);
}
void
ndr_rpc_set_nonull(mlrpc_handle_t *handle)
{
handle->clnt->nonull = B_TRUE;
}
int
ndr_rpc_get_ssnkey(mlrpc_handle_t *handle, uchar_t *key, size_t len)
{
ndr_client_t *clnt = handle->clnt;
if (clnt == NULL || clnt->xa_fd == -1)
return (EINVAL);
return (smb_fh_getssnkey(clnt->xa_fd, key, len));
}
void *
ndr_rpc_malloc(mlrpc_handle_t *handle, size_t size)
{
ndr_heap_t *heap;
if ((heap = ndr_rpc_get_heap(handle)) == NULL)
return (NULL);
return (ndr_heap_malloc(heap, size));
}
ndr_heap_t *
ndr_rpc_get_heap(mlrpc_handle_t *handle)
{
ndr_client_t *clnt = handle->clnt;
if (clnt->heap == NULL)
clnt->heap = ndr_heap_create();
return (clnt->heap);
}
void
ndr_rpc_release(mlrpc_handle_t *handle)
{
ndr_client_t *clnt = handle->clnt;
if (clnt->heap_preserved)
ndr_clnt_free_heap(clnt);
else
ndr_heap_destroy(clnt->heap);
clnt->heap = NULL;
}
boolean_t
ndr_is_null_handle(mlrpc_handle_t *handle)
{
static const ndr_hdid_t hdid0 = {0};
if (handle == NULL || handle->clnt == NULL)
return (B_TRUE);
if (!memcmp(&handle->handle, &hdid0, sizeof (hdid0)))
return (B_TRUE);
return (B_FALSE);
}
boolean_t
ndr_is_bind_handle(mlrpc_handle_t *handle)
{
return (handle->clnt->handle == &handle->handle);
}
void
ndr_inherit_handle(mlrpc_handle_t *child, mlrpc_handle_t *parent)
{
child->clnt = parent->clnt;
}
static int
ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa)
{
ndr_stream_t *recv_nds = &mxa->recv_nds;
ndr_stream_t *send_nds = &mxa->send_nds;
ndr_heap_t *heap = clnt->heap;
int rc;
if (heap == NULL) {
if ((heap = ndr_heap_create()) == NULL)
return (-1);
clnt->heap = heap;
}
mxa->heap = heap;
rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
if (rc == 0)
rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
NDR_MODE_RETURN_RECV, heap);
if (rc != 0) {
nds_destruct(&mxa->recv_nds);
nds_destruct(&mxa->send_nds);
ndr_heap_destroy(mxa->heap);
mxa->heap = NULL;
clnt->heap = NULL;
return (-1);
}
if (clnt->nonull)
NDS_SETF(send_nds, NDS_F_NONULL);
return (0);
}
static int
ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa)
{
ndr_stream_t *recv_nds = &mxa->recv_nds;
ndr_stream_t *send_nds = &mxa->send_nds;
int err, more, nbytes;
nbytes = recv_nds->pdu_max_size;
err = smb_fh_xactnp(clnt->xa_fd,
send_nds->pdu_size, (char *)send_nds->pdu_base_offset,
&nbytes, (char *)recv_nds->pdu_base_offset, &more);
if (err) {
recv_nds->pdu_size = 0;
return (-1);
}
recv_nds->pdu_size = nbytes;
return (0);
}
static int
ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa)
{
ndr_stream_t *nds = &mxa->recv_nds;
int len;
int nbytes;
if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0)
return (-1);
nbytes = smb_fh_read(clnt->xa_fd, 0, len,
(char *)nds->pdu_base_offset + nds->pdu_size);
if (nbytes < 0)
return (-1);
nds->pdu_size += nbytes;
if (nds->pdu_size > nds->pdu_max_size) {
nds->pdu_size = nds->pdu_max_size;
return (-1);
}
return (nbytes);
}
static void
ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa)
{
assert(clnt->heap == mxa->heap);
clnt->heap_preserved = B_TRUE;
mxa->heap = NULL;
}
static void
ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa)
{
nds_destruct(&mxa->recv_nds);
nds_destruct(&mxa->send_nds);
if (!clnt->heap_preserved) {
ndr_heap_destroy(mxa->heap);
mxa->heap = NULL;
clnt->heap = NULL;
}
}
static void
ndr_xa_release(ndr_client_t *clnt)
{
if (clnt->heap_preserved) {
ndr_heap_destroy(clnt->heap);
clnt->heap = NULL;
clnt->heap_preserved = B_FALSE;
}
}