BSD 4_3_Tahoe release
[unix-history] / usr / src / new / sunrpc / clnt_udp.c
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
#ifndef lint
static char sccsid[] = "@(#)clnt_udp.c 1.5 85/03/17 Copyr 1984 Sun Micro";
#endif
/*
* clnt_udp.c, Implements a UPD/IP based, client side RPC.
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*/
#include <stdio.h>
#include "types.h"
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include "xdr.h"
#include "auth.h"
#include "clnt.h"
#include "rpc_msg.h"
#include "pmap_clnt.h"
char *malloc();
extern int errno;
long random();
/*
* UDP bases client side rpc operations
*/
static enum clnt_stat clntudp_call();
static void clntudp_abort();
static void clntudp_geterr();
static bool_t clntudp_freeres();
static void clntudp_destroy();
static struct clnt_ops udp_ops = {
clntudp_call,
clntudp_abort,
clntudp_geterr,
clntudp_freeres,
clntudp_destroy
};
/*
* Private data kept per client handle
*/
struct cu_data {
int cu_sock;
struct sockaddr_in cu_raddr;
int cu_rlen;
struct timeval cu_wait;
struct rpc_err cu_error;
XDR cu_outxdrs;
u_int cu_xdrpos;
char cu_outbuf[UDPMSGSIZE];
char cu_inbuf[UDPMSGSIZE];
};
/*
* Create a UDP based client handle.
* If *sockp<0, *sockp is set to a newly created UPD socket.
* If raddr->sin_port is 0 a binder on the remote machine
* is consulted for the correct port number.
* NB: It is the clients responsibility to close *sockp.
* NB: The rpch->cl_auth is initialized to null authentication.
* Caller may wish to set this something more useful.
*
* wait is the amount of time used between retransmitting a call if
* no response has been heard; retransmition occurs until the actual
* rpc call times out.
*/
CLIENT *
clntudp_create(raddr, program, version, wait, sockp)
struct sockaddr_in *raddr;
u_long program;
u_long version;
struct timeval wait;
register int *sockp;
{
CLIENT *cl;
register struct cu_data *cu;
struct timeval now;
struct rpc_msg call_msg;
cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
if (cl == NULL) {
fprintf(stderr, "clntudp_create: out of memory\n");
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
goto fooy;
}
cu = (struct cu_data *)mem_alloc(sizeof(*cu));
if (cu == NULL) {
fprintf(stderr, "clntudp_create: out of memory\n");
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
goto fooy;
}
(void)gettimeofday(&now, (struct timezone *)0);
if (raddr->sin_port == 0) {
u_short port;
if ((port =
pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
goto fooy;
}
raddr->sin_port = htons(port);
}
cl->cl_ops = &udp_ops;
cl->cl_private = (caddr_t)cu;
cu->cu_raddr = *raddr;
cu->cu_rlen = sizeof (cu->cu_raddr);
cu->cu_wait = wait;
call_msg.rm_xid = getpid() ^ (int)random() ^ now.tv_sec ^ now.tv_usec;
call_msg.rm_direction = CALL;
call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
call_msg.rm_call.cb_prog = program;
call_msg.rm_call.cb_vers = version;
xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
UDPMSGSIZE, XDR_ENCODE);
if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
goto fooy;
}
cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
cu->cu_sock = (*sockp < 0) ?
(*sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) :
*sockp;
if (cu->cu_sock < 0) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
goto fooy;
}
cl->cl_auth = authnone_create();
return (cl);
fooy:
mem_free((caddr_t)cu, sizeof(*cu));
mem_free((caddr_t)cl, sizeof(CLIENT));
return ((CLIENT *)NULL);
}
static enum clnt_stat
clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, timeout)
register CLIENT *cl; /* client handle */
u_long proc; /* procedure number */
xdrproc_t xargs; /* xdr routine for args */
caddr_t argsp; /* pointer to args */
xdrproc_t xresults; /* xdr routine for results */
caddr_t resultsp; /* pointer to results */
struct timeval timeout; /* seconds to wait before giving up */
{
register struct cu_data *cu = (struct cu_data *)cl->cl_private;
register XDR *xdrs;
register int outlen;
register int inlen;
int readfds, fromlen;
register int mask;
struct sockaddr_in from;
struct rpc_msg reply_msg;
XDR reply_xdrs;
struct timeval time_waited;
bool_t ok;
call_again:
time_waited.tv_sec = 0;
time_waited.tv_usec = 0;
xdrs = &(cu->cu_outxdrs);
xdrs->x_op = XDR_ENCODE;
XDR_SETPOS(xdrs, cu->cu_xdrpos);
/*
* the transaction is the first thing in the out buffer
*/
(*(u_short *)(cu->cu_outbuf))++;
if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
(! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
(! (*xargs)(xdrs, argsp)))
return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
outlen = (int)XDR_GETPOS(xdrs);
while (TRUE) {
if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
(struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
!= outlen) {
cu->cu_error.re_errno = errno;
return (cu->cu_error.re_status = RPC_CANTSEND);
}
/*
* sub-optimal code appears inside the loop because we have
* some clock time to spare while the packets are in flight.
* (We assume that this is actually only executed once.)
*/
reply_msg.acpted_rply.ar_verf = _null_auth;
reply_msg.acpted_rply.ar_results.where = resultsp;
reply_msg.acpted_rply.ar_results.proc = xresults;
mask = 1 << cu->cu_sock;
rcv_again:
readfds = mask;
switch (select(32, &readfds, (int *)NULL, (int *)NULL,
&(cu->cu_wait))) {
case 0:
time_waited.tv_sec += cu->cu_wait.tv_sec;
time_waited.tv_usec += cu->cu_wait.tv_usec;
while (time_waited.tv_usec >= 1000000) {
time_waited.tv_sec++;
time_waited.tv_usec -= 1000000;
}
if ((time_waited.tv_sec < timeout.tv_sec) ||
((time_waited.tv_sec == timeout.tv_sec) &&
(time_waited.tv_usec < timeout.tv_usec)))
continue;
return (cu->cu_error.re_status = RPC_TIMEDOUT);
case -1:
if (errno == EINTR)
goto rcv_again;
cu->cu_error.re_errno = errno;
return (cu->cu_error.re_status = RPC_CANTRECV);
}
if ((readfds & mask) == 0)
goto rcv_again;
tryagain:
fromlen = sizeof(struct sockaddr);
inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, UDPMSGSIZE, 0,
(struct sockaddr *)&from, &fromlen);
if (inlen < 0) {
if (errno == EINTR)
goto tryagain;
cu->cu_error.re_errno = errno;
return (cu->cu_error.re_status = RPC_CANTRECV);
}
if (inlen < sizeof(u_long))
goto rcv_again;
/* see if reply transaction id matches sent id */
if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
goto rcv_again;
/* we now assume we have the proper reply */
break;
}
/*
* now decode and validate the response
*/
xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
ok = xdr_replymsg(&reply_xdrs, &reply_msg);
/* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
if (ok) {
_seterr_reply(&reply_msg, &(cu->cu_error));
if (cu->cu_error.re_status == RPC_SUCCESS) {
if (! AUTH_VALIDATE(cl->cl_auth,
&reply_msg.acpted_rply.ar_verf)) {
cu->cu_error.re_status = RPC_AUTHERROR;
cu->cu_error.re_why = AUTH_INVALIDRESP;
}
if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
xdrs->x_op = XDR_FREE;
(void)xdr_opaque_auth(xdrs,
&(reply_msg.acpted_rply.ar_verf));
}
} /* end successful completion */
else {
/* maybe our credentials need to be refreshed ... */
if (AUTH_REFRESH(cl->cl_auth))
goto call_again;
} /* end of unsuccessful completion */
} /* end of valid reply message */
else {
cu->cu_error.re_status = RPC_CANTDECODERES;
}
return (cu->cu_error.re_status);
}
static void
clntudp_geterr(cl, errp)
CLIENT *cl;
struct rpc_err *errp;
{
register struct cu_data *cu = (struct cu_data *)cl->cl_private;
*errp = cu->cu_error;
}
static bool_t
clntudp_freeres(cl, xdr_res, res_ptr)
CLIENT *cl;
xdrproc_t xdr_res;
caddr_t res_ptr;
{
register struct cu_data *cu = (struct cu_data *)cl->cl_private;
register XDR *xdrs = &(cu->cu_outxdrs);
xdrs->x_op = XDR_FREE;
return ((*xdr_res)(xdrs, res_ptr));
}
static void
clntudp_abort(/*h*/)
/*CLIENT *h;*/
{
}
static void
clntudp_destroy(cl)
CLIENT *cl;
{
register struct cu_data *cu = (struct cu_data *)cl->cl_private;
XDR_DESTROY(&(cu->cu_outxdrs));
mem_free((caddr_t)cu, sizeof(*cu));
mem_free((caddr_t)cl, sizeof(CLIENT));
}