Home | History | Annotate | Download | only in librpcsoc
      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 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Copyright (c) 1984, 1986, 1987, 1988, 1989, 1996 AT&T
     29  * All Rights Reserved
     30  */
     31 
     32 /*
     33  * University Copyright- Copyright (c) 1982, 1986, 1988
     34  * The Regents of the University of California
     35  * All Rights Reserved
     36  *
     37  * University Acknowledgment- Portions of this document are derived from
     38  * software developed by the University of California, Berkeley, and its
     39  * contributors.
     40  */
     41 
     42 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     43 
     44 /*
     45  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
     46  */
     47 
     48 #include <rpc/rpc.h>
     49 #include <sys/socket.h>
     50 #include <sys/time.h>
     51 #include <sys/ioctl.h>
     52 #include <netdb.h>
     53 #include <errno.h>
     54 #include <rpc/pmap_clnt.h>
     55 #include <rpc/clnt_soc.h>
     56 #include <syslog.h>
     57 #include <sys/filio.h>
     58 #include <malloc.h>
     59 #include <unistd.h>
     60 #include <stropts.h>
     61 #include <stdio.h>
     62 
     63 
     64 extern int errno;
     65 
     66 extern int _socket(int, int, int);
     67 extern pid_t getpid();
     68 extern int bindresvport(int, struct sockaddr_in *);
     69 extern bool_t   xdr_opaque_auth(XDR *, struct opaque_auth *);
     70 extern int _sendto(int, const char *, int, int,
     71 	const struct sockaddr *, int);
     72 extern int _recvfrom(int, char *, int, int,
     73 	struct sockaddr *, int *);
     74 
     75 
     76 static struct clnt_ops *clntudp_ops();
     77 
     78 /*
     79  * Private data kept per client handle
     80  */
     81 struct cu_data {
     82 	int		   cu_sock;
     83 	bool_t		   cu_closeit;
     84 	struct sockaddr_in cu_raddr;
     85 	int		   cu_rlen;
     86 	struct timeval	   cu_wait;
     87 	struct timeval	   cu_total;
     88 	struct rpc_err	   cu_error;
     89 	XDR		   cu_outxdrs;
     90 	u_int		   cu_xdrpos;
     91 	u_int		   cu_sendsz;
     92 	char		   *cu_outbuf;
     93 	u_int		   cu_recvsz;
     94 	char		   cu_inbuf[1];
     95 };
     96 
     97 /*
     98  * Create a UDP based client handle.
     99  * If *sockp<0, *sockp is set to a newly created UPD socket.
    100  * If raddr->sin_port is 0 a binder on the remote machine
    101  * is consulted for the correct port number.
    102  * NB: It is the clients responsibility to close *sockp.
    103  * NB: The rpch->cl_auth is initialized to null authentication.
    104  *	Caller may wish to set this something more useful.
    105  *
    106  * wait is the amount of time used between retransmitting a call if
    107  * no response has been heard;  retransmition occurs until the actual
    108  * rpc call times out.
    109  *
    110  * sendsz and recvsz are the maximum allowable packet sizes that can be
    111  * sent and received.
    112  */
    113 CLIENT *
    114 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
    115 	struct sockaddr_in *raddr;
    116 	rpcprog_t program;
    117 	rpcvers_t version;
    118 	struct timeval wait;
    119 	register int *sockp;
    120 	u_int sendsz;
    121 	u_int recvsz;
    122 {
    123 	CLIENT *cl;
    124 	register struct cu_data *cu;
    125 	struct timeval now;
    126 	struct rpc_msg call_msg;
    127 
    128 	cl = (CLIENT *)mem_alloc(sizeof (CLIENT));
    129 	if (cl == NULL) {
    130 		(void) syslog(LOG_ERR, "clntudp_create: out of memory");
    131 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
    132 		rpc_createerr.cf_error.re_errno = errno;
    133 		goto fooy;
    134 	}
    135 	sendsz = ((sendsz + 3) / 4) * 4;
    136 	recvsz = ((recvsz + 3) / 4) * 4;
    137 	cu = (struct cu_data *)mem_alloc(sizeof (*cu) + sendsz + recvsz);
    138 	if (cu == NULL) {
    139 		(void) syslog(LOG_ERR, "clntudp_create: out of memory");
    140 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
    141 		rpc_createerr.cf_error.re_errno = errno;
    142 		goto fooy;
    143 	}
    144 	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
    145 
    146 	(void) gettimeofday(&now, (struct timezone *)0);
    147 	if (raddr->sin_port == 0) {
    148 		u_short port;
    149 		if ((port =
    150 		    pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
    151 			goto fooy;
    152 		}
    153 		raddr->sin_port = htons(port);
    154 	}
    155 	cl->cl_ops = clntudp_ops();
    156 	cl->cl_private = (caddr_t)cu;
    157 	cu->cu_raddr = *raddr;
    158 	cu->cu_rlen = sizeof (cu->cu_raddr);
    159 	cu->cu_wait = wait;
    160 	cu->cu_total.tv_sec = -1;
    161 	cu->cu_total.tv_usec = -1;
    162 	cu->cu_sendsz = sendsz;
    163 	cu->cu_recvsz = recvsz;
    164 	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
    165 	call_msg.rm_direction = CALL;
    166 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
    167 	call_msg.rm_call.cb_prog = program;
    168 	call_msg.rm_call.cb_vers = version;
    169 	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
    170 	    sendsz, XDR_ENCODE);
    171 	if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
    172 		goto fooy;
    173 	}
    174 	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
    175 	if (*sockp < 0) {
    176 		int dontblock = 1;
    177 
    178 		*sockp = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    179 		if (*sockp < 0) {
    180 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
    181 			rpc_createerr.cf_error.re_errno = errno;
    182 			goto fooy;
    183 		}
    184 		/* attempt to bind to prov port */
    185 		(void) bindresvport(*sockp, (struct sockaddr_in *)0);
    186 		/* the sockets rpc controls are non-blocking */
    187 		(void) ioctl(*sockp, FIONBIO, (char *) &dontblock);
    188 		cu->cu_closeit = TRUE;
    189 	} else {
    190 		cu->cu_closeit = FALSE;
    191 	}
    192 	cu->cu_sock = *sockp;
    193 	cl->cl_auth = authnone_create();
    194 	return (cl);
    195 fooy:
    196 	if (cu)
    197 		mem_free((caddr_t)cu, sizeof (*cu) + sendsz + recvsz);
    198 	if (cl)
    199 		mem_free((caddr_t)cl, sizeof (CLIENT));
    200 	return ((CLIENT *)NULL);
    201 }
    202 
    203 CLIENT *
    204 clntudp_create(raddr, program, version, wait, sockp)
    205 	struct sockaddr_in *raddr;
    206 	rpcprog_t program;
    207 	rpcvers_t version;
    208 	struct timeval wait;
    209 	register int *sockp;
    210 {
    211 
    212 	return (clntudp_bufcreate(raddr, program, version, wait, sockp,
    213 	    UDPMSGSIZE, UDPMSGSIZE));
    214 }
    215 
    216 static enum clnt_stat
    217 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
    218 	register CLIENT	*cl;		/* client handle */
    219 	rpcproc_t	proc;		/* procedure number */
    220 	xdrproc_t	xargs;		/* xdr routine for args */
    221 	caddr_t		argsp;		/* pointer to args */
    222 	xdrproc_t	xresults;	/* xdr routine for results */
    223 	caddr_t		resultsp;	/* pointer to results */
    224 	struct timeval	utimeout;	/* seconds to wait before giving up */
    225 {
    226 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
    227 	register XDR *xdrs;
    228 	register int outlen;
    229 	register int inlen;
    230 	int fromlen;
    231 	fd_set readfds;
    232 	fd_set mask;
    233 	struct sockaddr_in from;
    234 	struct rpc_msg reply_msg;
    235 	XDR reply_xdrs;
    236 	struct timeval startime, curtime;
    237 	int firsttimeout = 1;
    238 	struct timeval time_waited;
    239 	struct timeval retransmit_time;
    240 	bool_t ok;
    241 	int nrefreshes = 2;	/* number of times to refresh cred */
    242 	struct timeval timeout;
    243 
    244 	if (cu->cu_total.tv_usec == -1) {
    245 		timeout = utimeout;	/* use supplied timeout */
    246 	} else {
    247 		timeout = cu->cu_total; /* use default timeout */
    248 	}
    249 
    250 	time_waited.tv_sec = 0;
    251 	time_waited.tv_usec = 0;
    252 	retransmit_time = cu->cu_wait;
    253 
    254 call_again:
    255 	xdrs = &(cu->cu_outxdrs);
    256 	xdrs->x_op = XDR_ENCODE;
    257 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
    258 	/*
    259 	 * the transaction is the first thing in the out buffer
    260 	 */
    261 	(*(u_short *)(cu->cu_outbuf))++;
    262 	if ((! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
    263 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
    264 	    (! (*xargs)(xdrs, argsp)))
    265 		return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
    266 	outlen = (int)XDR_GETPOS(xdrs);
    267 
    268 send_again:
    269 	if (_sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
    270 	    (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
    271 	    != outlen) {
    272 		cu->cu_error.re_errno = errno;
    273 		return (cu->cu_error.re_status = RPC_CANTSEND);
    274 	}
    275 
    276 	/*
    277 	 * Hack to provide rpc-based message passing
    278 	 */
    279 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
    280 		return (cu->cu_error.re_status = RPC_TIMEDOUT);
    281 	}
    282 	/*
    283 	 * sub-optimal code appears here because we have
    284 	 * some clock time to spare while the packets are in flight.
    285 	 * (We assume that this is actually only executed once.)
    286 	 */
    287 	reply_msg.acpted_rply.ar_verf = _null_auth;
    288 	reply_msg.acpted_rply.ar_results.where = resultsp;
    289 	reply_msg.acpted_rply.ar_results.proc = xresults;
    290 	FD_ZERO(&mask);
    291 	FD_SET(cu->cu_sock, &mask);
    292 	for (;;) {
    293 		readfds = mask;
    294 		switch (select(__rpc_dtbsize(), &readfds, NULL,
    295 		    NULL, &(retransmit_time))) {
    296 
    297 		case 0:
    298 			time_waited.tv_sec += retransmit_time.tv_sec;
    299 			time_waited.tv_usec += retransmit_time.tv_usec;
    300 			while (time_waited.tv_usec >= 1000000) {
    301 				time_waited.tv_sec++;
    302 				time_waited.tv_usec -= 1000000;
    303 			}
    304 
    305 			/* update retransmit_time */
    306 
    307 			if (retransmit_time.tv_sec < RPC_MAX_BACKOFF) {
    308 			retransmit_time.tv_usec += retransmit_time.tv_usec;
    309 			retransmit_time.tv_sec += retransmit_time.tv_sec;
    310 			while (retransmit_time.tv_usec >= 1000000) {
    311 				retransmit_time.tv_sec++;
    312 				retransmit_time.tv_usec -= 1000000;
    313 				}
    314 			}
    315 
    316 			if ((time_waited.tv_sec < timeout.tv_sec) ||
    317 				((time_waited.tv_sec == timeout.tv_sec) &&
    318 				(time_waited.tv_usec < timeout.tv_usec)))
    319 				goto send_again;
    320 			return (cu->cu_error.re_status = RPC_TIMEDOUT);
    321 
    322 		/*
    323 		 * buggy in other cases because time_waited is not being
    324 		 * updated.
    325 		 */
    326 		case -1:
    327 			if (errno != EINTR) {
    328 				cu->cu_error.re_errno = errno;
    329 				return (cu->cu_error.re_status = RPC_CANTRECV);
    330 			}
    331 
    332 			/* interrupted by another signal, update time_waited */
    333 			if (firsttimeout) {
    334 				/*
    335 				 * Could have done gettimeofday before clnt_call
    336 				 * but that means 1 more system call per each
    337 				 * clnt_call, so do it after first time out
    338 				 */
    339 				if (gettimeofday(&startime,
    340 					(struct timezone *) NULL) == -1) {
    341 					errno = 0;
    342 					continue;
    343 				}
    344 				firsttimeout = 0;
    345 				errno = 0;
    346 				continue;
    347 			};
    348 
    349 			if (gettimeofday(&curtime,
    350 				(struct timezone *) NULL) == -1) {
    351 				errno = 0;
    352 				continue;
    353 			};
    354 
    355 			time_waited.tv_sec += curtime.tv_sec - startime.tv_sec;
    356 			time_waited.tv_usec += curtime.tv_usec -
    357 							startime.tv_usec;
    358 			while (time_waited.tv_usec < 0) {
    359 				time_waited.tv_sec--;
    360 				time_waited.tv_usec += 1000000;
    361 			};
    362 			while (time_waited.tv_usec >= 1000000) {
    363 				time_waited.tv_sec++;
    364 				time_waited.tv_usec -= 1000000;
    365 			}
    366 			startime.tv_sec = curtime.tv_sec;
    367 			startime.tv_usec = curtime.tv_usec;
    368 			if ((time_waited.tv_sec > timeout.tv_sec) ||
    369 				((time_waited.tv_sec == timeout.tv_sec) &&
    370 				(time_waited.tv_usec > timeout.tv_usec))) {
    371 				return (cu->cu_error.re_status = RPC_TIMEDOUT);
    372 			}
    373 			errno = 0; /* reset it */
    374 			continue;
    375 
    376 		}
    377 		do {
    378 			fromlen = sizeof (struct sockaddr);
    379 			inlen = _recvfrom(cu->cu_sock, cu->cu_inbuf,
    380 				(int) cu->cu_recvsz, 0,
    381 				(struct sockaddr *)&from, &fromlen);
    382 		} while (inlen < 0 && errno == EINTR);
    383 		if (inlen < 0) {
    384 			if (errno == EWOULDBLOCK)
    385 				continue;
    386 			cu->cu_error.re_errno = errno;
    387 			return (cu->cu_error.re_status = RPC_CANTRECV);
    388 		}
    389 		if (inlen < sizeof (uint32_t))
    390 			continue;
    391 		/* see if reply transaction id matches sent id */
    392 		if (*((uint32_t *)(cu->cu_inbuf)) !=
    393 				*((uint32_t *)(cu->cu_outbuf)))
    394 			continue;
    395 		/* we now assume we have the proper reply */
    396 		break;
    397 	}
    398 
    399 	/*
    400 	 * now decode and validate the response
    401 	 */
    402 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
    403 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
    404 	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
    405 	if (ok) {
    406 		__seterr_reply(&reply_msg, &(cu->cu_error));
    407 		if (cu->cu_error.re_status == RPC_SUCCESS) {
    408 			if (! AUTH_VALIDATE(cl->cl_auth,
    409 				&reply_msg.acpted_rply.ar_verf)) {
    410 				cu->cu_error.re_status = RPC_AUTHERROR;
    411 				cu->cu_error.re_why = AUTH_INVALIDRESP;
    412 			}
    413 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
    414 				xdrs->x_op = XDR_FREE;
    415 				(void) xdr_opaque_auth(xdrs,
    416 				    &(reply_msg.acpted_rply.ar_verf));
    417 			}
    418 		}  /* end successful completion */
    419 		else {
    420 			/* maybe our credentials need to be refreshed ... */
    421 			if (nrefreshes > 0 &&
    422 				AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
    423 				nrefreshes--;
    424 				goto call_again;
    425 			}
    426 		}  /* end of unsuccessful completion */
    427 	}  /* end of valid reply message */
    428 	else {
    429 		cu->cu_error.re_status = RPC_CANTDECODERES;
    430 	}
    431 	return (cu->cu_error.re_status);
    432 }
    433 
    434 static void
    435 clntudp_geterr(cl, errp)
    436 	CLIENT *cl;
    437 	struct rpc_err *errp;
    438 {
    439 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
    440 
    441 	*errp = cu->cu_error;
    442 }
    443 
    444 
    445 static bool_t
    446 clntudp_freeres(cl, xdr_res, res_ptr)
    447 	CLIENT *cl;
    448 	xdrproc_t xdr_res;
    449 	caddr_t res_ptr;
    450 {
    451 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
    452 	register XDR *xdrs = &(cu->cu_outxdrs);
    453 
    454 	xdrs->x_op = XDR_FREE;
    455 	return ((*xdr_res)(xdrs, res_ptr));
    456 }
    457 
    458 static void
    459 clntudp_abort()
    460 	/* CLIENT *h; */
    461 {
    462 }
    463 
    464 static bool_t
    465 clntudp_control(cl, request, info)
    466 	CLIENT *cl;
    467 	int request;
    468 	char *info;
    469 {
    470 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
    471 
    472 	switch (request) {
    473 	case CLSET_TIMEOUT:
    474 		cu->cu_total = *(struct timeval *)info;
    475 		break;
    476 	case CLGET_TIMEOUT:
    477 		*(struct timeval *)info = cu->cu_total;
    478 		break;
    479 	case CLSET_RETRY_TIMEOUT:
    480 		cu->cu_wait = *(struct timeval *)info;
    481 		break;
    482 	case CLGET_RETRY_TIMEOUT:
    483 		*(struct timeval *)info = cu->cu_wait;
    484 		break;
    485 	case CLGET_SERVER_ADDR:
    486 		*(struct sockaddr_in *)info = cu->cu_raddr;
    487 		break;
    488 	case CLGET_FD:
    489 		*(int *)info = cu->cu_sock;
    490 		break;
    491 	case CLSET_FD_CLOSE:
    492 		cu->cu_closeit = TRUE;
    493 		break;
    494 	case CLSET_FD_NCLOSE:
    495 		cu->cu_closeit = FALSE;
    496 		break;
    497 	default:
    498 		return (FALSE);
    499 	}
    500 	return (TRUE);
    501 }
    502 
    503 static void
    504 clntudp_destroy(cl)
    505 	CLIENT *cl;
    506 {
    507 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
    508 
    509 	if (cu->cu_closeit) {
    510 		(void) close(cu->cu_sock);
    511 	}
    512 	XDR_DESTROY(&(cu->cu_outxdrs));
    513 	mem_free((caddr_t)cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
    514 	mem_free((caddr_t)cl, sizeof (CLIENT));
    515 }
    516 
    517 static struct clnt_ops *
    518 clntudp_ops()
    519 {
    520 	static struct clnt_ops ops;
    521 
    522 	if (ops.cl_call == NULL) {
    523 		ops.cl_call = clntudp_call;
    524 		ops.cl_abort = clntudp_abort;
    525 		ops.cl_geterr = clntudp_geterr;
    526 		ops.cl_freeres = clntudp_freeres;
    527 		ops.cl_destroy = clntudp_destroy;
    528 		ops.cl_control = clntudp_control;
    529 	}
    530 	return (&ops);
    531 }
    532