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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * clnt_doors.c, Client side for doors IPC based RPC. 29 */ 30 31 #include "mt.h" 32 #include "rpc_mt.h" 33 #include <rpc/rpc.h> 34 #include <errno.h> 35 #include <sys/poll.h> 36 #include <syslog.h> 37 #include <sys/types.h> 38 #include <sys/kstat.h> 39 #include <sys/time.h> 40 #include <door.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <string.h> 44 #include <alloca.h> 45 #include <rpc/svc_mt.h> 46 #include <sys/mman.h> 47 #include <atomic.h> 48 49 50 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *); 51 52 static struct clnt_ops *clnt_door_ops(); 53 54 extern int __rpc_default_door_buf_size; 55 extern int __rpc_min_door_buf_size; 56 57 /* 58 * Private data kept per client handle 59 */ 60 struct cu_data { 61 int cu_fd; /* door fd */ 62 bool_t cu_closeit; /* close it on destroy */ 63 struct rpc_err cu_error; 64 uint_t cu_xdrpos; 65 uint_t cu_sendsz; /* send size */ 66 char cu_header[32]; /* precreated header */ 67 }; 68 69 /* 70 * Door IPC based client creation routine. 71 * 72 * NB: The rpch->cl_auth is initialized to null authentication. 73 * Caller may wish to set this something more useful. 74 * 75 * sendsz is the maximum allowable packet size that can be sent. 76 * 0 will cause default to be used. 77 */ 78 CLIENT * 79 clnt_door_create(const rpcprog_t program, const rpcvers_t version, 80 const uint_t sendsz) 81 { 82 CLIENT *cl = NULL; /* client handle */ 83 struct cu_data *cu = NULL; /* private data */ 84 struct rpc_msg call_msg; 85 char rendezvous[64]; 86 int did; 87 struct door_info info; 88 XDR xdrs; 89 struct timeval now; 90 uint_t ssz; 91 92 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)program, 93 (int)version); 94 if ((did = open(rendezvous, O_RDONLY, 0)) < 0) { 95 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 96 rpc_createerr.cf_error.re_errno = errno; 97 rpc_createerr.cf_error.re_terrno = 0; 98 return (NULL); 99 } 100 101 if (door_info(did, &info) < 0 || (info.di_attributes & DOOR_REVOKED)) { 102 (void) close(did); 103 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 104 rpc_createerr.cf_error.re_errno = errno; 105 rpc_createerr.cf_error.re_terrno = 0; 106 return (NULL); 107 } 108 109 /* 110 * Determine send size 111 */ 112 if (sendsz < __rpc_min_door_buf_size) 113 ssz = __rpc_default_door_buf_size; 114 else 115 ssz = RNDUP(sendsz); 116 117 if ((cl = malloc(sizeof (CLIENT))) == NULL || 118 (cu = malloc(sizeof (*cu))) == NULL) { 119 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 120 rpc_createerr.cf_error.re_errno = errno; 121 goto err; 122 } 123 124 /* 125 * Precreate RPC header for performance reasons. 126 */ 127 (void) gettimeofday(&now, NULL); 128 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 129 call_msg.rm_call.cb_prog = program; 130 call_msg.rm_call.cb_vers = version; 131 xdrmem_create(&xdrs, cu->cu_header, sizeof (cu->cu_header), XDR_ENCODE); 132 if (!xdr_callhdr(&xdrs, &call_msg)) { 133 rpc_createerr.cf_stat = RPC_CANTENCODEARGS; 134 rpc_createerr.cf_error.re_errno = 0; 135 goto err; 136 } 137 cu->cu_xdrpos = XDR_GETPOS(&xdrs); 138 139 cu->cu_sendsz = ssz; 140 cu->cu_fd = did; 141 cu->cu_closeit = TRUE; 142 cl->cl_ops = clnt_door_ops(); 143 cl->cl_private = (caddr_t)cu; 144 cl->cl_auth = authnone_create(); 145 cl->cl_tp = strdup(rendezvous); 146 if (cl->cl_tp == NULL) { 147 syslog(LOG_ERR, "clnt_door_create: strdup failed"); 148 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 149 rpc_createerr.cf_error.re_errno = errno; 150 goto err; 151 } 152 cl->cl_netid = strdup("door"); 153 if (cl->cl_netid == NULL) { 154 syslog(LOG_ERR, "clnt_door_create: strdup failed"); 155 if (cl->cl_tp) 156 free(cl->cl_tp); 157 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 158 rpc_createerr.cf_error.re_errno = errno; 159 goto err; 160 } 161 return (cl); 162 err: 163 rpc_createerr.cf_error.re_terrno = 0; 164 if (cl) { 165 free(cl); 166 if (cu) 167 free(cu); 168 } 169 (void) close(did); 170 return (NULL); 171 } 172 173 /* ARGSUSED */ 174 static enum clnt_stat 175 clnt_door_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, 176 xdrproc_t xresults, caddr_t resultsp, struct timeval utimeout) 177 { 178 /* LINTED pointer alignment */ 179 struct cu_data *cu = (struct cu_data *)cl->cl_private; 180 XDR xdrs; 181 door_arg_t params; 182 char *outbuf_ref; 183 struct rpc_msg reply_msg; 184 bool_t need_to_unmap; 185 uint32_t xid; 186 int nrefreshes = 2; /* number of times to refresh cred */ 187 188 rpc_callerr.re_errno = 0; 189 rpc_callerr.re_terrno = 0; 190 191 if ((params.rbuf = alloca(cu->cu_sendsz)) == NULL) { 192 rpc_callerr.re_terrno = 0; 193 rpc_callerr.re_errno = errno; 194 return (rpc_callerr.re_status = RPC_SYSTEMERROR); 195 } 196 outbuf_ref = params.rbuf; 197 params.rsize = cu->cu_sendsz; 198 if ((params.data_ptr = alloca(cu->cu_sendsz)) == NULL) { 199 rpc_callerr.re_terrno = 0; 200 rpc_callerr.re_errno = errno; 201 return (rpc_callerr.re_status = RPC_SYSTEMERROR); 202 } 203 204 call_again: 205 xdrmem_create(&xdrs, params.data_ptr, cu->cu_sendsz, XDR_ENCODE); 206 /* Increment XID (not really needed for RPC over doors...) */ 207 /* LINTED pointer alignment */ 208 xid = atomic_inc_uint_nv((uint32_t *)cu->cu_header); 209 (void) memcpy(params.data_ptr, cu->cu_header, cu->cu_xdrpos); 210 /* LINTED pointer alignment */ 211 *(uint32_t *)params.data_ptr = xid; 212 XDR_SETPOS(&xdrs, cu->cu_xdrpos); 213 214 if ((!XDR_PUTINT32(&xdrs, (int32_t *)&proc)) || 215 (!AUTH_MARSHALL(cl->cl_auth, &xdrs)) || 216 (!(*xargs)(&xdrs, argsp))) { 217 return (rpc_callerr.re_status = RPC_CANTENCODEARGS); 218 } 219 params.data_size = (int)XDR_GETPOS(&xdrs); 220 221 params.desc_ptr = NULL; 222 params.desc_num = 0; 223 if (door_call(cu->cu_fd, ¶ms) < 0) { 224 rpc_callerr.re_errno = errno; 225 return (rpc_callerr.re_status = RPC_CANTSEND); 226 } 227 228 if (params.rbuf == NULL || params.rsize == 0) { 229 return (rpc_callerr.re_status = RPC_FAILED); 230 } 231 need_to_unmap = (params.rbuf != outbuf_ref); 232 233 /* LINTED pointer alignment */ 234 if (*(uint32_t *)params.rbuf != xid) { 235 rpc_callerr.re_status = RPC_CANTDECODERES; 236 goto done; 237 } 238 239 xdrmem_create(&xdrs, params.rbuf, params.rsize, XDR_DECODE); 240 reply_msg.acpted_rply.ar_verf = _null_auth; 241 reply_msg.acpted_rply.ar_results.where = resultsp; 242 reply_msg.acpted_rply.ar_results.proc = xresults; 243 244 if (xdr_replymsg(&xdrs, &reply_msg)) { 245 if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED && 246 reply_msg.acpted_rply.ar_stat == SUCCESS) 247 rpc_callerr.re_status = RPC_SUCCESS; 248 else 249 __seterr_reply(&reply_msg, &rpc_callerr); 250 251 if (rpc_callerr.re_status == RPC_SUCCESS) { 252 if (!AUTH_VALIDATE(cl->cl_auth, 253 &reply_msg.acpted_rply.ar_verf)) { 254 rpc_callerr.re_status = RPC_AUTHERROR; 255 rpc_callerr.re_why = AUTH_INVALIDRESP; 256 } 257 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 258 xdrs.x_op = XDR_FREE; 259 (void) xdr_opaque_auth(&xdrs, 260 &(reply_msg.acpted_rply.ar_verf)); 261 } 262 } 263 /* 264 * If unsuccesful AND error is an authentication error 265 * then refresh credentials and try again, else break 266 */ 267 else if (rpc_callerr.re_status == RPC_AUTHERROR) { 268 /* 269 * maybe our credentials need to be refreshed ... 270 */ 271 if (nrefreshes-- && 272 AUTH_REFRESH(cl->cl_auth, &reply_msg)) { 273 if (need_to_unmap) 274 (void) munmap(params.rbuf, 275 params.rsize); 276 goto call_again; 277 } else 278 /* 279 * We are setting rpc_callerr here given that 280 * libnsl is not reentrant thereby 281 * reinitializing the TSD. If not set here then 282 * success could be returned even though refresh 283 * failed. 284 */ 285 rpc_callerr.re_status = RPC_AUTHERROR; 286 } 287 } else 288 rpc_callerr.re_status = RPC_CANTDECODERES; 289 290 done: 291 if (need_to_unmap) 292 (void) munmap(params.rbuf, params.rsize); 293 return (rpc_callerr.re_status); 294 } 295 296 /* ARGSUSED */ 297 static enum clnt_stat 298 clnt_door_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp) 299 { 300 /* send() call not supported on doors */ 301 302 rpc_callerr.re_errno = ENOTSUP; 303 rpc_callerr.re_terrno = 0; 304 305 return (rpc_callerr.re_status = RPC_FAILED); 306 } 307 308 static void 309 clnt_door_geterr(CLIENT *cl, struct rpc_err *errp) 310 { 311 /* LINTED pointer alignment */ 312 struct cu_data *cu = (struct cu_data *)cl->cl_private; 313 314 *errp = rpc_callerr; 315 } 316 317 /* ARGSUSED */ 318 static bool_t 319 clnt_door_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr) 320 { 321 XDR xdrs; 322 323 (void) memset(&xdrs, 0, sizeof (xdrs)); 324 xdrs.x_op = XDR_FREE; 325 return ((*xdr_res)(&xdrs, res_ptr)); 326 } 327 328 static void 329 clnt_door_abort(CLIENT *cl) 330 { 331 cl = cl; 332 } 333 334 static bool_t 335 clnt_door_control(CLIENT *cl, int request, char *info) 336 { 337 /* LINTED pointer alignment */ 338 struct cu_data *cu = (struct cu_data *)cl->cl_private; 339 340 switch (request) { 341 case CLSET_FD_CLOSE: 342 cu->cu_closeit = TRUE; 343 return (TRUE); 344 345 case CLSET_FD_NCLOSE: 346 cu->cu_closeit = FALSE; 347 return (TRUE); 348 } 349 350 /* for other requests which use info */ 351 if (info == NULL) 352 return (FALSE); 353 354 switch (request) { 355 case CLGET_FD: 356 /* LINTED pointer alignment */ 357 *(int *)info = cu->cu_fd; 358 break; 359 360 case CLGET_XID: 361 /* 362 * use the knowledge that xid is the 363 * first element in the call structure *. 364 * This will get the xid of the PREVIOUS call 365 */ 366 /* LINTED pointer alignment */ 367 *(uint32_t *)info = ntohl(*(uint32_t *)cu->cu_header); 368 break; 369 370 case CLSET_XID: 371 /* This will set the xid of the NEXT call */ 372 /* LINTED pointer alignment */ 373 *(uint32_t *)cu->cu_header = htonl(*(uint32_t *)info - 1); 374 /* decrement by 1 as clnt_door_call() increments once */ 375 break; 376 377 case CLGET_VERS: 378 /* 379 * This RELIES on the information that, in the call body, 380 * the version number field is the fifth field from the 381 * begining of the RPC header. MUST be changed if the 382 * call_struct is changed 383 */ 384 /* LINTED pointer alignment */ 385 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header + 386 4 * BYTES_PER_XDR_UNIT)); 387 break; 388 389 case CLSET_VERS: 390 /* LINTED pointer alignment */ 391 *(uint32_t *)(cu->cu_header + 4 * BYTES_PER_XDR_UNIT) = 392 /* LINTED pointer alignment */ 393 htonl(*(uint32_t *)info); 394 break; 395 396 case CLGET_PROG: 397 /* 398 * This RELIES on the information that, in the call body, 399 * the program number field is the fourth field from the 400 * begining of the RPC header. MUST be changed if the 401 * call_struct is changed 402 */ 403 /* LINTED pointer alignment */ 404 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header + 405 3 * BYTES_PER_XDR_UNIT)); 406 break; 407 408 case CLSET_PROG: 409 /* LINTED pointer alignment */ 410 *(uint32_t *)(cu->cu_header + 3 * BYTES_PER_XDR_UNIT) = 411 /* LINTED pointer alignment */ 412 htonl(*(uint32_t *)info); 413 break; 414 415 default: 416 return (FALSE); 417 } 418 return (TRUE); 419 } 420 421 static void 422 clnt_door_destroy(CLIENT *cl) 423 { 424 /* LINTED pointer alignment */ 425 struct cu_data *cu = (struct cu_data *)cl->cl_private; 426 int cu_fd = cu->cu_fd; 427 428 if (cu->cu_closeit) 429 (void) close(cu_fd); 430 free(cu); 431 if (cl->cl_netid && cl->cl_netid[0]) 432 free(cl->cl_netid); 433 if (cl->cl_tp && cl->cl_tp[0]) 434 free(cl->cl_tp); 435 free(cl); 436 } 437 438 static struct clnt_ops * 439 clnt_door_ops(void) 440 { 441 static struct clnt_ops ops; 442 extern mutex_t ops_lock; 443 444 sig_mutex_lock(&ops_lock); 445 if (ops.cl_call == NULL) { 446 ops.cl_call = clnt_door_call; 447 ops.cl_send = clnt_door_send; 448 ops.cl_abort = clnt_door_abort; 449 ops.cl_geterr = clnt_door_geterr; 450 ops.cl_freeres = clnt_door_freeres; 451 ops.cl_destroy = clnt_door_destroy; 452 ops.cl_control = clnt_door_control; 453 } 454 sig_mutex_unlock(&ops_lock); 455 return (&ops); 456 } 457 458 int 459 _update_did(CLIENT *cl, int vers) 460 { 461 /* LINTED pointer alignment */ 462 struct cu_data *cu = (struct cu_data *)cl->cl_private; 463 rpcprog_t prog; 464 char rendezvous[64]; 465 466 if (cu->cu_fd >= 0) 467 (void) close(cu->cu_fd); 468 /* Make sure that the right door id is used in door_call. */ 469 clnt_control(cl, CLGET_PROG, (void *)&prog); 470 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)prog, vers); 471 if ((cu->cu_fd = open(rendezvous, O_RDONLY, 0)) < 0) { 472 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 473 rpc_createerr.cf_error.re_errno = errno; 474 rpc_createerr.cf_error.re_terrno = 0; 475 return (0); 476 } 477 free(cl->cl_tp); 478 cl->cl_tp = strdup(rendezvous); 479 if (cl->cl_tp == NULL) { 480 syslog(LOG_ERR, "_update_did: strdup failed"); 481 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 482 rpc_createerr.cf_error.re_errno = errno; 483 rpc_createerr.cf_error.re_terrno = 0; 484 return (0); 485 } 486 return (1); 487 } 488