diff options
Diffstat (limited to 'usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c')
-rw-r--r-- | usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c new file mode 100644 index 0000000000..0f58231251 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c @@ -0,0 +1,368 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/errno.h> +#include <string.h> +#include <strings.h> + +#include <smbsrv/libsmb.h> +#include <smbsrv/ndr.h> +#include <smbsrv/mlrpc.h> + +#define MLRPC_IS_LAST_FRAG(F) ((F) & MLRPC_PFC_LAST_FRAG) +#define MLRPC_DEFAULT_FRAGSZ 8192 + +static void mlrpc_c_init_hdr(struct mlrpc_client *, struct mlrpc_xaction *); +static int mlrpc_c_get_frags(struct mlrpc_client *, struct mlrpc_xaction *); +static void mlrpc_c_remove_hdr(struct mlndr_stream *, int *); + +int +mlrpc_c_bind(struct mlrpc_client *mcli, char *service_name, + struct mlrpc_binding **ret_binding_p) +{ + struct mlrpc_service *msvc; + struct mlrpc_binding *mbind; + struct mlrpc_xaction mxa; + mlrpcconn_bind_hdr_t *bhdr; + mlrpc_p_cont_elem_t *pce; + mlrpcconn_bind_ack_hdr_t *bahdr; + mlrpc_p_result_t *pre; + int rc; + + bzero(&mxa, sizeof (mxa)); + + msvc = mlrpc_find_service_by_name(service_name); + if (msvc == NULL) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + mxa.binding_list = mcli->binding_list; + if ((mbind = mlrpc_new_binding(&mxa)) == NULL) + return (MLRPC_DRC_FAULT_API_BIND_NO_SLOTS); + + mlrpc_c_init_hdr(mcli, &mxa); + + bhdr = &mxa.send_hdr.bind_hdr; + bhdr->common_hdr.ptype = MLRPC_PTYPE_BIND; + bhdr->common_hdr.frag_length = sizeof (*bhdr); + bhdr->max_xmit_frag = MLRPC_DEFAULT_FRAGSZ; + bhdr->max_recv_frag = MLRPC_DEFAULT_FRAGSZ; + bhdr->assoc_group_id = 0; + bhdr->p_context_elem.n_context_elem = 1; + + /* Assign presentation context id */ + pce = &bhdr->p_context_elem.p_cont_elem[0]; + pce->p_cont_id = mcli->next_p_cont_id++; + pce->n_transfer_syn = 1; + + /* Set up UUIDs and versions from the service */ + pce->abstract_syntax.if_version = msvc->abstract_syntax_version; + rc = mlrpc_str_to_uuid(msvc->abstract_syntax_uuid, + &pce->abstract_syntax.if_uuid); + if (!rc) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; + rc = mlrpc_str_to_uuid(msvc->transfer_syntax_uuid, + &pce->transfer_syntaxes[0].if_uuid); + if (!rc) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + /* Format and exchange the PDU */ + + rc = (*mcli->xa_init)(mcli, &mxa, 0); + if (MLRPC_DRC_IS_FAULT(rc)) + return (rc); + + rc = mlrpc_encode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_exchange)(mcli, &mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = mlrpc_decode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + /* done with buffers */ + (*mcli->xa_destruct)(mcli, &mxa); + + bahdr = &mxa.recv_hdr.bind_ack_hdr; + + if (mxa.ptype != MLRPC_PTYPE_BIND_ACK) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + if (bahdr->p_result_list.n_results != 1) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + pre = &bahdr->p_result_list.p_results[0]; + + if (pre->result != MLRPC_PCDR_ACCEPTANCE) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + mbind->p_cont_id = pce->p_cont_id; + mbind->which_side = MLRPC_BIND_SIDE_CLIENT; + mbind->context = mcli; + mbind->service = msvc; + mbind->instance_specific = 0; + + *ret_binding_p = mbind; + return (MLRPC_DRC_OK); + +fault_exit: + (*mcli->xa_destruct)(mcli, &mxa); + return (rc); +} + +int +mlrpc_c_call(struct mlrpc_binding *mbind, int opnum, void *params, + mlrpc_heapref_t *heapref) +{ + struct mlrpc_client *mcli = mbind->context; + struct mlrpc_service *msvc = mbind->service; + struct mlrpc_xaction mxa; + mlrpcconn_request_hdr_t *reqhdr; + mlrpcconn_common_header_t *rsphdr; + unsigned long recv_pdu_scan_offset; + int rc; + + if (mlrpc_find_stub_in_svc(msvc, opnum) == NULL) + return (MLRPC_DRC_FAULT_API_OPNUM_INVALID); + + bzero(&mxa, sizeof (mxa)); + mxa.ptype = MLRPC_PTYPE_REQUEST; + mxa.opnum = opnum; + mxa.binding = mbind; + + mlrpc_c_init_hdr(mcli, &mxa); + + reqhdr = &mxa.send_hdr.request_hdr; + reqhdr->common_hdr.ptype = MLRPC_PTYPE_REQUEST; + reqhdr->p_cont_id = mbind->p_cont_id; + reqhdr->opnum = opnum; + + rc = (*mcli->xa_init)(mcli, &mxa, heapref->heap); + if (MLRPC_DRC_IS_FAULT(rc)) + return (rc); + + /* Reserve room for hdr */ + mxa.send_mlnds.pdu_scan_offset = sizeof (*reqhdr); + + rc = mlrpc_encode_call(&mxa, params); + if (!MLRPC_DRC_IS_OK(rc)) + goto fault_exit; + + mxa.send_mlnds.pdu_scan_offset = 0; + + /* + * Now we have the PDU size, we need to set up the + * frag_length and calculate the alloc_hint. + */ + mxa.send_hdr.common_hdr.frag_length = mxa.send_mlnds.pdu_size; + reqhdr->alloc_hint = mxa.send_mlnds.pdu_size - + sizeof (mlrpcconn_request_hdr_t); + + rc = mlrpc_encode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_exchange)(mcli, &mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = mlrpc_decode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + if (mxa.ptype != MLRPC_PTYPE_RESPONSE) { + rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } + + rsphdr = &mxa.recv_hdr.common_hdr; + + if (!MLRPC_IS_LAST_FRAG(rsphdr->pfc_flags)) { + /* + * This is a multi-fragment response. + * Preserve the current scan offset while getting + * fragments so that we can continue afterward + * as if we had received the entire response as + * a single PDU. + */ + recv_pdu_scan_offset = mxa.recv_mlnds.pdu_scan_offset; + + if (mlrpc_c_get_frags(mcli, &mxa) < 0) { + rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } + + mxa.recv_mlnds.pdu_scan_offset = recv_pdu_scan_offset; + } + + rc = mlrpc_decode_return(&mxa, params); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_preserve)(mcli, &mxa, heapref); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + (*mcli->xa_destruct)(mcli, &mxa); + return (MLRPC_DRC_OK); + +fault_exit: + (*mcli->xa_destruct)(mcli, &mxa); + return (rc); +} + +void +mlrpc_c_free_heap(struct mlrpc_binding *mbind, mlrpc_heapref_t *heapref) +{ + struct mlrpc_client *mcli = mbind->context; + + (*mcli->xa_release)(mcli, heapref); +} + +static void +mlrpc_c_init_hdr(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + + hdr->rpc_vers = 5; + hdr->rpc_vers_minor = 0; + hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII; +#ifndef _BIG_ENDIAN + hdr->packed_drep.intg_char_rep |= MLRPC_REPLAB_INTG_LITTLE_ENDIAN; +#endif + /* hdr->frag_length */ + hdr->auth_length = 0; + hdr->call_id = mcli->next_call_id++; +} + +/* + * mlrpc_c_remove_hdr + * + * Remove an RPC fragment header from the received data stream. + * + * Original RPC receive buffer: + * |- frag1 -| |-frag M(partial)-| + * +==================+=============+----+=================+ + * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | + * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | + * +-----+------------+-------------+ +-----+-----------+ + * | hdr | data | data | .. | hdr | data | + * +=====+============+=============+----+=====+===========+ + * <------ + * ^ ^ ^ + * | | | + * base_offset hdr data + * + * |-------------------------------------|-----------------| + * offset len + * + * RPC receive buffer (after this call): + * +==================+=============+----+===========+ + * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | + * | (with RPC hdr) | Rsp2 | .. | RspN | + * +-----+------------+-------------+ +-----------+ + * | hdr | data | data | .. | data | + * +=====+============+=============+----+===========+ + */ +static void +mlrpc_c_remove_hdr(struct mlndr_stream *mlnds, int *nbytes) +{ + char *hdr; + char *data; + + hdr = (char *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset; + data = hdr + MLRPC_RSP_HDR_SIZE; + *nbytes -= MLRPC_RSP_HDR_SIZE; + + bcopy(data, hdr, *nbytes); + mlnds->pdu_size -= MLRPC_RSP_HDR_SIZE; +} + +/* + * mlrpc_c_get_frags + * + * A DCE RPC message that is larger than a single fragment is transmitted + * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for + * both Windows 2000 and 2003. + * + * Collect RPC fragments and append them to the receive stream buffer. + * Each received fragment has a header, which we need to remove as we + * build the full RPC PDU. + * + * The xa_read() calls will translate to SmbReadX requests. Note that + * there is no correspondence between SmbReadX buffering and DCE RPC + * fragment alignment. + * + * Return -1 on error. Otherwise, return the total data count of the + * complete RPC response upon success. + */ +static int +mlrpc_c_get_frags(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + struct mlndr_stream *mlnds = &mxa->recv_mlnds; + mlrpcconn_common_header_t hdr; + int frag_rcvd; + int frag_size; + int last_frag; + int nbytes; + + /* + * The scan offest will be used to locate the frag header. + */ + mlnds->pdu_scan_offset = mlnds->pdu_base_offset + mlnds->pdu_size; + + do { + frag_rcvd = 0; + + do { + if ((nbytes = (*mcli->xa_read)(mcli, mxa)) < 0) + return (-1); + + if (frag_rcvd == 0) { + mlrpc_decode_frag_hdr(mlnds, &hdr); + + last_frag = MLRPC_IS_LAST_FRAG(hdr.pfc_flags); + frag_size = hdr.frag_length + - MLRPC_RSP_HDR_SIZE; + + mlrpc_c_remove_hdr(mlnds, &nbytes); + mlnds->pdu_scan_offset += frag_size; + } + + frag_rcvd += nbytes; + + } while (frag_rcvd < frag_size); + } while (!last_frag); + + return (0); +} |