1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 #include <sys/errno.h> 29 #include <string.h> 30 #include <strings.h> 31 32 #include <libmlrpc.h> 33 34 #define NDR_DEFAULT_FRAGSZ 8192 35 #define NDR_MULTI_FRAGSZ (60 * 1024) 36 37 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 38 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 39 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); 40 41 int 42 ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, 43 ndr_binding_t **ret_binding_p) 44 { 45 ndr_binding_t *mbind; 46 ndr_xa_t mxa; 47 ndr_bind_hdr_t *bhdr; 48 ndr_p_cont_elem_t *pce; 49 ndr_bind_ack_hdr_t *bahdr; 50 ndr_p_result_t *pre; 51 int rc; 52 53 bzero(&mxa, sizeof (mxa)); 54 55 mxa.binding_list = clnt->binding_list; 56 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 57 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 58 59 ndr_clnt_init_hdr(clnt, &mxa); 60 61 bhdr = &mxa.send_hdr.bind_hdr; 62 bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 63 bhdr->common_hdr.frag_length = sizeof (*bhdr); 64 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 65 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 66 bhdr->assoc_group_id = 0; 67 bhdr->p_context_elem.n_context_elem = 1; 68 69 /* Assign presentation context id */ 70 pce = &bhdr->p_context_elem.p_cont_elem[0]; 71 pce->p_cont_id = clnt->next_p_cont_id++; 72 pce->n_transfer_syn = 1; 73 74 /* Set up UUIDs and versions from the service */ 75 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 76 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 77 &pce->abstract_syntax.if_uuid); 78 if (rc != 0) 79 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 80 81 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 82 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 83 &pce->transfer_syntaxes[0].if_uuid); 84 if (rc != 0) 85 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 86 87 /* Format and exchange the PDU */ 88 89 if ((*clnt->xa_init)(clnt, &mxa) < 0) 90 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 91 92 rc = ndr_encode_pdu_hdr(&mxa); 93 if (NDR_DRC_IS_FAULT(rc)) 94 goto fault_exit; 95 96 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 97 rc = NDR_DRC_FAULT_SEND_FAILED; 98 goto fault_exit; 99 } 100 101 rc = ndr_decode_pdu_hdr(&mxa); 102 if (NDR_DRC_IS_FAULT(rc)) 103 goto fault_exit; 104 105 /* done with buffers */ 106 (*clnt->xa_destruct)(clnt, &mxa); 107 108 bahdr = &mxa.recv_hdr.bind_ack_hdr; 109 110 if (mxa.ptype != NDR_PTYPE_BIND_ACK) 111 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 112 113 if (bahdr->p_result_list.n_results != 1) 114 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 115 116 pre = &bahdr->p_result_list.p_results[0]; 117 118 if (pre->result != NDR_PCDR_ACCEPTANCE) 119 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 120 121 mbind->p_cont_id = pce->p_cont_id; 122 mbind->which_side = NDR_BIND_SIDE_CLIENT; 123 mbind->clnt = clnt; 124 mbind->service = msvc; 125 mbind->instance_specific = 0; 126 127 *ret_binding_p = mbind; 128 return (NDR_DRC_OK); 129 130 fault_exit: 131 (*clnt->xa_destruct)(clnt, &mxa); 132 return (rc); 133 } 134 135 int 136 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 137 { 138 ndr_client_t *clnt = mbind->clnt; 139 ndr_xa_t mxa; 140 ndr_request_hdr_t *reqhdr; 141 ndr_common_header_t *rsphdr; 142 unsigned long recv_pdu_scan_offset; 143 int rc; 144 145 bzero(&mxa, sizeof (mxa)); 146 mxa.ptype = NDR_PTYPE_REQUEST; 147 mxa.opnum = opnum; 148 mxa.binding = mbind; 149 150 ndr_clnt_init_hdr(clnt, &mxa); 151 152 reqhdr = &mxa.send_hdr.request_hdr; 153 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 154 reqhdr->p_cont_id = mbind->p_cont_id; 155 reqhdr->opnum = opnum; 156 157 rc = (*clnt->xa_init)(clnt, &mxa); 158 if (NDR_DRC_IS_FAULT(rc)) 159 return (rc); 160 161 /* Reserve room for hdr */ 162 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 163 164 rc = ndr_encode_call(&mxa, params); 165 if (!NDR_DRC_IS_OK(rc)) 166 goto fault_exit; 167 168 mxa.send_nds.pdu_scan_offset = 0; 169 170 /* 171 * Now we have the PDU size, we need to set up the 172 * frag_length and calculate the alloc_hint. 173 */ 174 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 175 reqhdr->alloc_hint = mxa.send_nds.pdu_size - 176 sizeof (ndr_request_hdr_t); 177 178 rc = ndr_encode_pdu_hdr(&mxa); 179 if (NDR_DRC_IS_FAULT(rc)) 180 goto fault_exit; 181 182 rc = (*clnt->xa_exchange)(clnt, &mxa); 183 if (NDR_DRC_IS_FAULT(rc)) 184 goto fault_exit; 185 186 rc = ndr_decode_pdu_hdr(&mxa); 187 if (NDR_DRC_IS_FAULT(rc)) 188 goto fault_exit; 189 190 if (mxa.ptype != NDR_PTYPE_RESPONSE) { 191 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 192 goto fault_exit; 193 } 194 195 rsphdr = &mxa.recv_hdr.common_hdr; 196 197 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 198 /* 199 * This is a multi-fragment response. 200 * Preserve the current scan offset while getting 201 * fragments so that we can continue afterward 202 * as if we had received the entire response as 203 * a single PDU. 204 */ 205 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); 206 207 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 208 mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; 209 mxa.recv_nds.pdu_size = rsphdr->frag_length; 210 211 if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 212 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 213 goto fault_exit; 214 } 215 216 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 217 } 218 219 rc = ndr_decode_return(&mxa, params); 220 if (NDR_DRC_IS_FAULT(rc)) 221 goto fault_exit; 222 223 (*clnt->xa_preserve)(clnt, &mxa); 224 (*clnt->xa_destruct)(clnt, &mxa); 225 return (NDR_DRC_OK); 226 227 fault_exit: 228 (*clnt->xa_destruct)(clnt, &mxa); 229 return (rc); 230 } 231 232 void 233 ndr_clnt_free_heap(ndr_client_t *clnt) 234 { 235 (*clnt->xa_release)(clnt); 236 } 237 238 static void 239 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 240 { 241 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 242 243 hdr->rpc_vers = 5; 244 hdr->rpc_vers_minor = 0; 245 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 246 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 247 #ifndef _BIG_ENDIAN 248 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 249 #endif 250 /* hdr->frag_length */ 251 hdr->auth_length = 0; 252 hdr->call_id = clnt->next_call_id++; 253 } 254 255 /* 256 * ndr_clnt_get_frags 257 * 258 * A DCE RPC message that is larger than a single fragment is transmitted 259 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 260 * both Windows 2000 and 2003. 261 * 262 * Collect RPC fragments and append them to the receive stream buffer. 263 * Each received fragment has a header, which we need to remove as we 264 * build the full RPC PDU. The scan offset is used to track frag headers. 265 */ 266 static int 267 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 268 { 269 ndr_stream_t *nds = &mxa->recv_nds; 270 ndr_common_header_t hdr; 271 int frag_size; 272 int last_frag; 273 274 do { 275 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { 276 nds_show_state(nds); 277 return (-1); 278 } 279 280 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 281 frag_size = hdr.frag_length; 282 283 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { 284 nds_show_state(nds); 285 return (-1); 286 } 287 288 ndr_remove_frag_hdr(nds); 289 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; 290 } while (!last_frag); 291 292 return (0); 293 } 294 295 /* 296 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX 297 * requests. Note that there is no correspondence between SmbReadX buffering 298 * and DCE RPC fragment alignment. 299 */ 300 static int 301 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) 302 { 303 ndr_stream_t *nds = &mxa->recv_nds; 304 unsigned long available; 305 int nbytes = 0; 306 307 available = nds->pdu_size - nds->pdu_scan_offset; 308 309 while (available < NDR_RSP_HDR_SIZE) { 310 if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) 311 return (-1); 312 available += nbytes; 313 } 314 315 ndr_decode_frag_hdr(nds, hdr); 316 ndr_show_hdr(hdr); 317 318 while (available < hdr->frag_length) { 319 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 320 return (-1); 321 available += nbytes; 322 } 323 324 return (nbytes); 325 } 326