#if !defined(lint) && defined(SCCSIDS)
static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
#endif
#include <stdio.h>
#include <unistd.h>
#include <gssrpc/rpc.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <gssrpc/pmap_clnt.h>
#include <string.h>
#define MCALL_MSG_SIZE 24
static enum clnt_stat clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
xdrproc_t, void *, struct timeval);
static void clnttcp_abort(CLIENT *);
static void clnttcp_geterr(CLIENT *, struct rpc_err *);
static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *);
static bool_t clnttcp_control(CLIENT *, int, void *);
static void clnttcp_destroy(CLIENT *);
static struct clnt_ops tcp_ops = {
clnttcp_call,
clnttcp_abort,
clnttcp_geterr,
clnttcp_freeres,
clnttcp_destroy,
clnttcp_control
};
struct ct_data {
int ct_sock;
bool_t ct_closeit;
struct timeval ct_wait;
bool_t ct_waitset;
struct sockaddr_in ct_addr;
struct rpc_err ct_error;
union {
char ct_mcall[MCALL_MSG_SIZE];
uint32_t ct_mcalli;
} ct_u;
u_int ct_mpos;
XDR ct_xdrs;
};
static int readtcp(char *, caddr_t, int);
static int writetcp(char *, caddr_t, int);
CLIENT *
clnttcp_create(
struct sockaddr_in *raddr,
rpcprog_t prog,
rpcvers_t vers,
register int *sockp,
u_int sendsz,
u_int recvsz)
{
CLIENT *h;
register struct ct_data *ct = 0;
struct timeval now;
struct rpc_msg call_msg;
h = (CLIENT *)mem_alloc(sizeof(*h));
if (h == NULL) {
(void)fprintf(stderr, "clnttcp_create: out of memory\n");
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
goto fooy;
}
ct = (struct ct_data *)mem_alloc(sizeof(*ct));
if (ct == NULL) {
(void)fprintf(stderr, "clnttcp_create: out of memory\n");
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
goto fooy;
}
if (raddr->sin_port == 0) {
u_short port;
if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
mem_free((caddr_t)ct, sizeof(struct ct_data));
mem_free((caddr_t)h, sizeof(CLIENT));
return ((CLIENT *)NULL);
}
raddr->sin_port = htons(port);
}
if (*sockp < 0) {
*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
(void)bindresvport(*sockp, (struct sockaddr_in *)0);
if ((*sockp < 0)
|| (connect(*sockp, (struct sockaddr *)raddr,
sizeof(*raddr)) < 0)) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
(void)close(*sockp);
goto fooy;
}
ct->ct_closeit = TRUE;
} else {
ct->ct_closeit = FALSE;
}
ct->ct_sock = *sockp;
ct->ct_wait.tv_usec = 0;
ct->ct_waitset = FALSE;
ct->ct_addr = *raddr;
(void)gettimeofday(&now, (struct timezone *)0);
call_msg.rm_xid = getpid() ^ 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 = prog;
call_msg.rm_call.cb_vers = vers;
xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcall, MCALL_MSG_SIZE,
XDR_ENCODE);
if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
if (ct->ct_closeit) {
(void)close(*sockp);
}
goto fooy;
}
ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
XDR_DESTROY(&(ct->ct_xdrs));
xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
(caddr_t)ct, readtcp, writetcp);
h->cl_ops = &tcp_ops;
h->cl_private = (caddr_t) ct;
h->cl_auth = authnone_create();
return (h);
fooy:
mem_free((caddr_t)ct, sizeof(struct ct_data));
mem_free((caddr_t)h, sizeof(CLIENT));
return ((CLIENT *)NULL);
}
static enum clnt_stat
clnttcp_call(
register CLIENT *h,
rpcproc_t proc,
xdrproc_t xdr_args,
void * args_ptr,
xdrproc_t xdr_results,
void * results_ptr,
struct timeval timeout)
{
register struct ct_data *ct = (struct ct_data *) h->cl_private;
register XDR *xdrs = &(ct->ct_xdrs);
struct rpc_msg reply_msg;
uint32_t x_id;
uint32_t *msg_x_id = &ct->ct_u.ct_mcalli;
register bool_t shipnow;
int refreshes = 2;
long procl = proc;
if (!ct->ct_waitset) {
ct->ct_wait = timeout;
}
shipnow =
(xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
&& timeout.tv_usec == 0) ? FALSE : TRUE;
call_again:
xdrs->x_op = XDR_ENCODE;
ct->ct_error.re_status = RPC_SUCCESS;
x_id = ntohl(--(*msg_x_id));
if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcall, ct->ct_mpos)) ||
(! XDR_PUTLONG(xdrs, &procl)) ||
(! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
(! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) {
if (ct->ct_error.re_status == RPC_SUCCESS)
ct->ct_error.re_status = RPC_CANTENCODEARGS;
(void)xdrrec_endofrecord(xdrs, TRUE);
return (ct->ct_error.re_status);
}
if (! xdrrec_endofrecord(xdrs, shipnow))
return (ct->ct_error.re_status = RPC_CANTSEND);
if (! shipnow)
return (RPC_SUCCESS);
if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
return(ct->ct_error.re_status = RPC_TIMEDOUT);
}
xdrs->x_op = XDR_DECODE;
while (TRUE) {
reply_msg.acpted_rply.ar_verf = gssrpc__null_auth;
reply_msg.acpted_rply.ar_results.where = NULL;
reply_msg.acpted_rply.ar_results.proc = xdr_void;
if (! xdrrec_skiprecord(xdrs))
return (ct->ct_error.re_status);
if (! xdr_replymsg(xdrs, &reply_msg)) {
enum xdr_op op = xdrs->x_op;
xdrs->x_op = XDR_FREE;
xdr_replymsg(xdrs, &reply_msg);
xdrs->x_op = op;
if (ct->ct_error.re_status == RPC_SUCCESS)
continue;
return (ct->ct_error.re_status);
}
if (reply_msg.rm_xid == x_id)
break;
}
gssrpc__seterr_reply(&reply_msg, &(ct->ct_error));
if (ct->ct_error.re_status == RPC_SUCCESS) {
if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
ct->ct_error.re_status = RPC_AUTHERROR;
ct->ct_error.re_why = AUTH_INVALIDRESP;
} else if (! AUTH_UNWRAP(h->cl_auth, xdrs,
xdr_results, results_ptr)) {
if (ct->ct_error.re_status == RPC_SUCCESS)
ct->ct_error.re_status = RPC_CANTDECODERES;
}
}
else {
if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
goto call_again;
}
if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
(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));
}
return (ct->ct_error.re_status);
}
static void
clnttcp_geterr(
CLIENT *h,
struct rpc_err *errp)
{
register struct ct_data *ct =
(struct ct_data *) h->cl_private;
*errp = ct->ct_error;
}
static bool_t
clnttcp_freeres(
CLIENT *cl,
xdrproc_t xdr_res,
void * res_ptr)
{
register struct ct_data *ct = (struct ct_data *)cl->cl_private;
register XDR *xdrs = &(ct->ct_xdrs);
xdrs->x_op = XDR_FREE;
return ((*xdr_res)(xdrs, res_ptr));
}
static void
clnttcp_abort(CLIENT *cl)
{
}
static bool_t
clnttcp_control(
CLIENT *cl,
int request,
void *info)
{
register struct ct_data *ct = (struct ct_data *)cl->cl_private;
int len;
switch (request) {
case CLSET_TIMEOUT:
ct->ct_wait = *(struct timeval *)info;
ct->ct_waitset = TRUE;
break;
case CLGET_TIMEOUT:
*(struct timeval *)info = ct->ct_wait;
break;
case CLGET_SERVER_ADDR:
*(struct sockaddr_in *)info = ct->ct_addr;
break;
case CLGET_LOCAL_ADDR:
len = sizeof(struct sockaddr);
if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0)
return FALSE;
else
return TRUE;
default:
return (FALSE);
}
return (TRUE);
}
static void
clnttcp_destroy(CLIENT *h)
{
register struct ct_data *ct =
(struct ct_data *) h->cl_private;
if (ct->ct_closeit) {
(void)close(ct->ct_sock);
}
XDR_DESTROY(&(ct->ct_xdrs));
mem_free((caddr_t)ct, sizeof(struct ct_data));
mem_free((caddr_t)h, sizeof(CLIENT));
}
static int
readtcp(
char *ctptr,
caddr_t buf,
register int len)
{
register struct ct_data *ct = (struct ct_data *)(void *)ctptr;
struct timeval tout;
#ifdef FD_SETSIZE
fd_set mask;
fd_set readfds;
if (len == 0)
return (0);
FD_ZERO(&mask);
FD_SET(ct->ct_sock, &mask);
#else
register int mask = 1 << (ct->ct_sock);
int readfds;
if (len == 0)
return (0);
#endif
while (TRUE) {
readfds = mask;
tout = ct->ct_wait;
switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL,
&tout)) {
case 0:
ct->ct_error.re_status = RPC_TIMEDOUT;
return (-1);
case -1:
if (errno == EINTR)
continue;
ct->ct_error.re_status = RPC_CANTRECV;
ct->ct_error.re_errno = errno;
return (-1);
}
break;
}
switch (len = read(ct->ct_sock, buf, (size_t) len)) {
case 0:
ct->ct_error.re_errno = ECONNRESET;
ct->ct_error.re_status = RPC_CANTRECV;
len = -1;
break;
case -1:
ct->ct_error.re_errno = errno;
ct->ct_error.re_status = RPC_CANTRECV;
break;
}
return (len);
}
static int
writetcp(
char *ctptr,
caddr_t buf,
int len)
{
struct ct_data *ct = (struct ct_data *)(void *)ctptr;
register int i, cnt;
for (cnt = len; cnt > 0; cnt -= i, buf += i) {
if ((i = write(ct->ct_sock, buf, (size_t) cnt)) == -1) {
ct->ct_error.re_errno = errno;
ct->ct_error.re_status = RPC_CANTSEND;
return (-1);
}
}
return (len);
}