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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * clnt_doors.c, Client side for doors IPC based RPC. 31 */ 32 33 #include "mt.h" 34 #include "rpc_mt.h" 35 #include <rpc/rpc.h> 36 #include <errno.h> 37 #include <sys/poll.h> 38 #include <syslog.h> 39 #include <sys/types.h> 40 #include <sys/kstat.h> 41 #include <sys/time.h> 42 #include <door.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <alloca.h> 47 #include <rpc/svc_mt.h> 48 49 50 extern bool_t xdr_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(program, version, sendsz) 80 rpcprog_t program; /* program number */ 81 rpcvers_t version; /* version number */ 82 uint_t sendsz; /* buffer recv size */ 83 { 84 CLIENT *cl = NULL; /* client handle */ 85 struct cu_data *cu = NULL; /* private data */ 86 struct rpc_msg call_msg; 87 char rendezvous[64]; 88 int did; 89 struct door_info info; 90 XDR xdrs; 91 struct timeval now; 92 93 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, program, 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 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 sendsz = __rpc_default_door_buf_size; 114 else 115 sendsz = RNDUP(sendsz); 116 117 if ((cl = (CLIENT *) mem_alloc(sizeof (CLIENT))) == (CLIENT *)NULL || 118 (cu = (struct cu_data *)mem_alloc(sizeof (*cu))) == 119 (struct cu_data *)NULL) { 120 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 121 rpc_createerr.cf_error.re_errno = errno; 122 goto err; 123 } 124 125 /* 126 * Precreate RPC header for performance reasons. 127 */ 128 (void) gettimeofday(&now, (struct timezone *)NULL); 129 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 130 call_msg.rm_call.cb_prog = program; 131 call_msg.rm_call.cb_vers = version; 132 xdrmem_create(&xdrs, cu->cu_header, sizeof (cu->cu_header), XDR_ENCODE); 133 if (!xdr_callhdr(&xdrs, &call_msg)) { 134 rpc_createerr.cf_stat = RPC_CANTENCODEARGS; 135 rpc_createerr.cf_error.re_errno = 0; 136 goto err; 137 } 138 cu->cu_xdrpos = XDR_GETPOS(&xdrs); 139 140 cu->cu_sendsz = sendsz; 141 cu->cu_fd = did; 142 cu->cu_closeit = TRUE; 143 cl->cl_ops = clnt_door_ops(); 144 cl->cl_private = (caddr_t)cu; 145 cl->cl_auth = authnone_create(); 146 cl->cl_tp = strdup(rendezvous); 147 if (cl->cl_tp == NULL) { 148 syslog(LOG_ERR, "clnt_door_create: strdup failed"); 149 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 150 rpc_createerr.cf_error.re_errno = errno; 151 goto err; 152 } 153 cl->cl_netid = strdup("door"); 154 if (cl->cl_netid == NULL) { 155 syslog(LOG_ERR, "clnt_door_create: strdup failed"); 156 if (cl->cl_tp) 157 free(cl->cl_tp); 158 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 159 rpc_createerr.cf_error.re_errno = errno; 160 goto err; 161 } 162 return (cl); 163 err: 164 rpc_createerr.cf_error.re_terrno = 0; 165 if (cl) { 166 mem_free((caddr_t)cl, sizeof (CLIENT)); 167 if (cu) 168 mem_free((caddr_t)cu, sizeof (*cu)); 169 } 170 close(did); 171 return ((CLIENT *)NULL); 172 } 173 174 /* ARGSUSED */ 175 static enum clnt_stat 176 clnt_door_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 177 CLIENT *cl; /* client handle */ 178 rpcproc_t proc; /* procedure number */ 179 xdrproc_t xargs; /* xdr routine for args */ 180 caddr_t argsp; /* pointer to args */ 181 xdrproc_t xresults; /* xdr routine for results */ 182 caddr_t resultsp; /* pointer to results */ 183 struct timeval utimeout; /* seconds to wait before giving up */ 184 { 185 /* LINTED pointer alignment */ 186 struct cu_data *cu = (struct cu_data *)cl->cl_private; 187 XDR xdrs; 188 door_arg_t params; 189 char *outbuf_ref; 190 struct rpc_msg reply_msg; 191 bool_t need_to_unmap; 192 int nrefreshes = 2; /* number of times to refresh cred */ 193 extern int munmap(); 194 195 rpc_callerr.re_errno = 0; 196 rpc_callerr.re_terrno = 0; 197 198 if ((params.rbuf = (char *)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 outbuf_ref = params.rbuf; 204 params.rsize = cu->cu_sendsz; 205 if ((params.data_ptr = (char *)alloca(cu->cu_sendsz)) == NULL) { 206 rpc_callerr.re_terrno = 0; 207 rpc_callerr.re_errno = errno; 208 return (rpc_callerr.re_status = RPC_SYSTEMERROR); 209 } 210 211 call_again: 212 xdrmem_create(&xdrs, params.data_ptr, cu->cu_sendsz, XDR_ENCODE); 213 /* LINTED pointer alignment */ 214 (*(uint32_t *)cu->cu_header)++; /* increment XID */ 215 memcpy(params.data_ptr, cu->cu_header, cu->cu_xdrpos); 216 XDR_SETPOS(&xdrs, cu->cu_xdrpos); 217 218 if ((!XDR_PUTINT32(&xdrs, (int32_t *)&proc)) || 219 (!AUTH_MARSHALL(cl->cl_auth, &xdrs)) || 220 (!(*xargs)(&xdrs, argsp))) { 221 return (rpc_callerr.re_status = RPC_CANTENCODEARGS); 222 } 223 params.data_size = (int)XDR_GETPOS(&xdrs); 224 225 params.desc_ptr = NULL; 226 params.desc_num = 0; 227 if (door_call(cu->cu_fd, ¶ms) < 0) { 228 rpc_callerr.re_errno = errno; 229 return (rpc_callerr.re_status = RPC_CANTSEND); 230 } 231 232 if (params.rbuf == NULL || params.rsize == 0) { 233 return (rpc_callerr.re_status = RPC_FAILED); 234 } 235 need_to_unmap = (params.rbuf != outbuf_ref); 236 237 /* LINTED pointer alignment */ 238 if (*(uint32_t *)params.rbuf != *(uint32_t *)cu->cu_header) { 239 rpc_callerr.re_status = RPC_CANTDECODERES; 240 goto done; 241 } 242 243 xdrmem_create(&xdrs, params.rbuf, params.rsize, XDR_DECODE); 244 reply_msg.acpted_rply.ar_verf = _null_auth; 245 reply_msg.acpted_rply.ar_results.where = resultsp; 246 reply_msg.acpted_rply.ar_results.proc = xresults; 247 248 if (xdr_replymsg(&xdrs, &reply_msg)) { 249 if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED && 250 reply_msg.acpted_rply.ar_stat == SUCCESS) 251 rpc_callerr.re_status = RPC_SUCCESS; 252 else 253 __seterr_reply(&reply_msg, &rpc_callerr); 254 255 if (rpc_callerr.re_status == RPC_SUCCESS) { 256 if (!AUTH_VALIDATE(cl->cl_auth, 257 &reply_msg.acpted_rply.ar_verf)) { 258 rpc_callerr.re_status = RPC_AUTHERROR; 259 rpc_callerr.re_why = AUTH_INVALIDRESP; 260 } 261 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 262 xdrs.x_op = XDR_FREE; 263 (void) xdr_opaque_auth(&xdrs, 264 &(reply_msg.acpted_rply.ar_verf)); 265 } 266 } 267 /* 268 * If unsuccesful AND error is an authentication error 269 * then refresh credentials and try again, else break 270 */ 271 else if (rpc_callerr.re_status == RPC_AUTHERROR) { 272 /* 273 * maybe our credentials need to be refreshed ... 274 */ 275 if (nrefreshes-- && 276 AUTH_REFRESH(cl->cl_auth, &reply_msg)) { 277 if (need_to_unmap) 278 munmap(params.rbuf, params.rsize); 279 goto call_again; 280 } else 281 /* 282 * We are setting rpc_callerr here given that 283 * libnsl is not reentrant thereby 284 * reinitializing the TSD. If not set here then 285 * success could be returned even though refresh 286 * failed. 287 */ 288 rpc_callerr.re_status = RPC_AUTHERROR; 289 } 290 } else 291 rpc_callerr.re_status = RPC_CANTDECODERES; 292 293 done: 294 if (need_to_unmap) 295 munmap(params.rbuf, params.rsize); 296 return (rpc_callerr.re_status); 297 } 298 299 /* ARGSUSED */ 300 static enum clnt_stat 301 clnt_door_send(cl, proc, xargs, argsp) 302 CLIENT *cl; /* client handle */ 303 rpcproc_t proc; /* procedure number */ 304 xdrproc_t xargs; /* xdr routine for args */ 305 caddr_t argsp; /* pointer to args */ 306 { 307 /* send() call not supported on doors */ 308 309 rpc_callerr.re_errno = ENOTSUP; 310 rpc_callerr.re_terrno = 0; 311 312 return (rpc_callerr.re_status = RPC_FAILED); 313 } 314 315 static void 316 clnt_door_geterr(cl, errp) 317 CLIENT *cl; 318 struct rpc_err *errp; 319 { 320 /* LINTED pointer alignment */ 321 struct cu_data *cu = (struct cu_data *)cl->cl_private; 322 323 *errp = rpc_callerr; 324 } 325 326 /* ARGSUSED */ 327 static bool_t 328 clnt_door_freeres(cl, xdr_res, res_ptr) 329 CLIENT *cl; 330 xdrproc_t xdr_res; 331 caddr_t res_ptr; 332 { 333 XDR xdrs; 334 335 memset(&xdrs, 0, sizeof (xdrs)); 336 xdrs.x_op = XDR_FREE; 337 return ((*xdr_res)(&xdrs, res_ptr)); 338 } 339 340 static void 341 clnt_door_abort(cl) 342 CLIENT *cl; 343 { 344 cl = cl; 345 } 346 347 static bool_t 348 clnt_door_control(cl, request, info) 349 CLIENT *cl; 350 int request; 351 char *info; 352 { 353 /* LINTED pointer alignment */ 354 struct cu_data *cu = (struct cu_data *)cl->cl_private; 355 356 switch (request) { 357 case CLSET_FD_CLOSE: 358 cu->cu_closeit = TRUE; 359 return (TRUE); 360 361 case CLSET_FD_NCLOSE: 362 cu->cu_closeit = FALSE; 363 return (TRUE); 364 } 365 366 /* for other requests which use info */ 367 if (info == NULL) 368 return (FALSE); 369 370 switch (request) { 371 case CLGET_FD: 372 /* LINTED pointer alignment */ 373 *(int *)info = cu->cu_fd; 374 break; 375 376 case CLGET_XID: 377 /* 378 * use the knowledge that xid is the 379 * first element in the call structure *. 380 * This will get the xid of the PREVIOUS call 381 */ 382 /* LINTED pointer alignment */ 383 *(uint32_t *)info = ntohl(*(uint32_t *)cu->cu_header); 384 break; 385 386 case CLSET_XID: 387 /* This will set the xid of the NEXT call */ 388 /* LINTED pointer alignment */ 389 *(uint32_t *)cu->cu_header = htonl(*(uint32_t *)info - 1); 390 /* decrement by 1 as clnt_door_call() increments once */ 391 break; 392 393 case CLGET_VERS: 394 /* 395 * This RELIES on the information that, in the call body, 396 * the version number field is the fifth field from the 397 * begining of the RPC header. MUST be changed if the 398 * call_struct is changed 399 */ 400 /* LINTED pointer alignment */ 401 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header + 402 4 * BYTES_PER_XDR_UNIT)); 403 break; 404 405 case CLSET_VERS: 406 /* LINTED pointer alignment */ 407 *(uint32_t *)(cu->cu_header + 4 * BYTES_PER_XDR_UNIT) = 408 /* LINTED pointer alignment */ 409 htonl(*(uint32_t *)info); 410 break; 411 412 case CLGET_PROG: 413 /* 414 * This RELIES on the information that, in the call body, 415 * the program number field is the fourth field from the 416 * begining of the RPC header. MUST be changed if the 417 * call_struct is changed 418 */ 419 /* LINTED pointer alignment */ 420 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header + 421 3 * BYTES_PER_XDR_UNIT)); 422 break; 423 424 case CLSET_PROG: 425 /* LINTED pointer alignment */ 426 *(uint32_t *)(cu->cu_header + 3 * BYTES_PER_XDR_UNIT) = 427 /* LINTED pointer alignment */ 428 htonl(*(uint32_t *)info); 429 break; 430 431 default: 432 return (FALSE); 433 } 434 return (TRUE); 435 } 436 437 static void 438 clnt_door_destroy(cl) 439 CLIENT *cl; 440 { 441 /* LINTED pointer alignment */ 442 struct cu_data *cu = (struct cu_data *)cl->cl_private; 443 int cu_fd = cu->cu_fd; 444 445 if (cu->cu_closeit) 446 close(cu_fd); 447 mem_free((caddr_t)cu, sizeof (*cu)); 448 if (cl->cl_netid && cl->cl_netid[0]) 449 mem_free(cl->cl_netid, strlen(cl->cl_netid) + 1); 450 if (cl->cl_tp && cl->cl_tp[0]) 451 mem_free(cl->cl_tp, strlen(cl->cl_tp) + 1); 452 mem_free((caddr_t)cl, sizeof (CLIENT)); 453 } 454 455 static struct clnt_ops * 456 clnt_door_ops() 457 { 458 static struct clnt_ops ops; 459 extern mutex_t ops_lock; 460 461 sig_mutex_lock(&ops_lock); 462 if (ops.cl_call == NULL) { 463 ops.cl_call = clnt_door_call; 464 ops.cl_send = clnt_door_send; 465 ops.cl_abort = clnt_door_abort; 466 ops.cl_geterr = clnt_door_geterr; 467 ops.cl_freeres = clnt_door_freeres; 468 ops.cl_destroy = clnt_door_destroy; 469 ops.cl_control = clnt_door_control; 470 } 471 sig_mutex_unlock(&ops_lock); 472 return (&ops); 473 } 474 475 int 476 _update_did(cl, vers) 477 CLIENT *cl; 478 int vers; 479 { 480 /* LINTED pointer alignment */ 481 struct cu_data *cu = (struct cu_data *)cl->cl_private; 482 rpcprog_t prog; 483 char rendezvous[64]; 484 485 if (cu->cu_fd >= 0) 486 close(cu->cu_fd); 487 /* Make sure that the right door id is used in door_call. */ 488 clnt_control(cl, CLGET_PROG, (void *)&prog); 489 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, prog, vers); 490 if ((cu->cu_fd = open(rendezvous, O_RDONLY, 0)) < 0) { 491 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 492 rpc_createerr.cf_error.re_errno = errno; 493 rpc_createerr.cf_error.re_terrno = 0; 494 return (0); 495 } 496 free(cl->cl_tp); 497 cl->cl_tp = strdup(rendezvous); 498 if (cl->cl_tp == NULL) { 499 syslog(LOG_ERR, "_update_did: strdup failed"); 500 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 501 rpc_createerr.cf_error.re_errno = errno; 502 rpc_createerr.cf_error.re_terrno = 0; 503 return (0); 504 } 505 return (1); 506 } 507