Path: blob/main/crypto/krb5/src/lib/rpc/clnt_udp.c
39537 views
/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */1/*2* Copyright (c) 2010, Oracle America, Inc.3*4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions are met:8*9* * Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* * Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in14* the documentation and/or other materials provided with the15* distribution.16*17* * Neither the name of the "Oracle America, Inc." nor the names of18* its contributors may be used to endorse or promote products19* derived from this software without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS22* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED23* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A24* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT25* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,26* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED27* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR28* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF29* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING30* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.32*/33#if !defined(lint) && defined(SCCSIDS)34static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";35#endif3637/*38* clnt_udp.c, Implements a UDP/IP based, client side RPC.39*/4041#include <stdio.h>42#include <unistd.h>43#include <gssrpc/rpc.h>44#include <sys/socket.h>45#include <sys/ioctl.h>46#if defined(sun)47#include <sys/filio.h>48#endif49#include <netdb.h>50#include <errno.h>51#include <string.h>52#include <gssrpc/pmap_clnt.h>53#include <port-sockets.h>54#include <errno.h>5556#ifndef GETSOCKNAME_ARG3_TYPE57#define GETSOCKNAME_ARG3_TYPE int58#endif5960/*61* UDP bases client side rpc operations62*/63static enum clnt_stat clntudp_call(CLIENT *, rpcproc_t, xdrproc_t, void *,64xdrproc_t, void *, struct timeval);65static void clntudp_abort(CLIENT *);66static void clntudp_geterr(CLIENT *, struct rpc_err *);67static bool_t clntudp_freeres(CLIENT *, xdrproc_t, void *);68static bool_t clntudp_control(CLIENT *, int, void *);69static void clntudp_destroy(CLIENT *);7071static struct clnt_ops udp_ops = {72clntudp_call,73clntudp_abort,74clntudp_geterr,75clntudp_freeres,76clntudp_destroy,77clntudp_control78};7980/*81* Private data kept per client handle82*/83struct cu_data {84SOCKET cu_sock;85bool_t cu_closeit;86struct sockaddr_in cu_raddr;87int cu_rlen;88struct sockaddr_in cu_laddr;89GETSOCKNAME_ARG3_TYPE cu_llen;90struct timeval cu_wait;91struct timeval cu_total;92struct rpc_err cu_error;93XDR cu_outxdrs;94u_int cu_xdrpos;95u_int cu_sendsz;96char *cu_outbuf;97u_int cu_recvsz;98char cu_inbuf[1];99};100101/*102* Create a UDP based client handle.103* If *sockp<0, *sockp is set to a newly created UPD socket.104* If raddr->sin_port is 0 a binder on the remote machine105* is consulted for the correct port number.106* NB: It is the clients responsibility to close *sockp.107* NB: The rpch->cl_auth is initialized to null authentication.108* Caller may wish to set this something more useful.109*110* wait is the amount of time used between retransmitting a call if111* no response has been heard; retransmission occurs until the actual112* rpc call times out.113*114* sendsz and recvsz are the maximum allowable packet sizes that can be115* sent and received.116*/117CLIENT *118clntudp_bufcreate(119struct sockaddr_in *raddr,120rpcprog_t program,121rpcvers_t version,122struct timeval wait,123int *sockp,124u_int sendsz,125u_int recvsz)126{127CLIENT *cl;128struct cu_data *cu = 0;129struct timeval now;130struct rpc_msg call_msg;131132cl = (CLIENT *)mem_alloc(sizeof(CLIENT));133if (cl == NULL) {134(void) fprintf(stderr, "clntudp_create: out of memory\n");135rpc_createerr.cf_stat = RPC_SYSTEMERROR;136rpc_createerr.cf_error.re_errno = errno;137goto fooy;138}139sendsz = ((sendsz + 3) / 4) * 4;140recvsz = ((recvsz + 3) / 4) * 4;141cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);142if (cu == NULL) {143(void) fprintf(stderr, "clntudp_create: out of memory\n");144rpc_createerr.cf_stat = RPC_SYSTEMERROR;145rpc_createerr.cf_error.re_errno = errno;146goto fooy;147}148cu->cu_outbuf = &cu->cu_inbuf[recvsz];149150(void)gettimeofday(&now, (struct timezone *)0);151if (raddr->sin_port == 0) {152u_short port;153if ((port =154pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {155goto fooy;156}157raddr->sin_port = htons(port);158}159cl->cl_ops = &udp_ops;160cl->cl_private = (caddr_t)cu;161cu->cu_raddr = *raddr;162cu->cu_rlen = sizeof (cu->cu_raddr);163cu->cu_wait = wait;164cu->cu_total.tv_sec = -1;165cu->cu_total.tv_usec = -1;166cu->cu_sendsz = sendsz;167cu->cu_recvsz = recvsz;168call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;169call_msg.rm_direction = CALL;170call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;171call_msg.rm_call.cb_prog = program;172call_msg.rm_call.cb_vers = version;173xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,174sendsz, XDR_ENCODE);175if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {176goto fooy;177}178cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));179if (*sockp < 0) {180int dontblock = 1;181182*sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);183if (*sockp < 0) {184rpc_createerr.cf_stat = RPC_SYSTEMERROR;185rpc_createerr.cf_error.re_errno = errno;186goto fooy;187}188/* attempt to bind to prov port */189(void)bindresvport_sa(*sockp, NULL);190/* the sockets rpc controls are non-blocking */191(void)ioctl(*sockp, FIONBIO, (char *) &dontblock);192cu->cu_closeit = TRUE;193} else {194cu->cu_closeit = FALSE;195}196if (connect(*sockp, (struct sockaddr *)raddr, sizeof(*raddr)) < 0)197goto fooy;198cu->cu_llen = sizeof(cu->cu_laddr);199if (getsockname(*sockp, (struct sockaddr *)&cu->cu_laddr, &cu->cu_llen) < 0)200goto fooy;201202cu->cu_sock = *sockp;203cl->cl_auth = authnone_create();204return (cl);205fooy:206if (cu)207mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);208if (cl)209mem_free((caddr_t)cl, sizeof(CLIENT));210return ((CLIENT *)NULL);211}212213CLIENT *214clntudp_create(215struct sockaddr_in *raddr,216rpcprog_t program,217rpcvers_t version,218struct timeval wait,219int *sockp)220{221222return(clntudp_bufcreate(raddr, program, version, wait, sockp,223UDPMSGSIZE, UDPMSGSIZE));224}225226static enum clnt_stat227clntudp_call(228CLIENT *cl, /* client handle */229rpcproc_t proc, /* procedure number */230xdrproc_t xargs, /* xdr routine for args */231void * argsp, /* pointer to args */232xdrproc_t xresults, /* xdr routine for results */233void * resultsp, /* pointer to results */234struct timeval utimeout /* seconds to wait before235* giving up */236)237{238struct cu_data *cu = (struct cu_data *)cl->cl_private;239XDR *xdrs;240int outlen;241ssize_t inlen;242GETSOCKNAME_ARG3_TYPE fromlen; /* Assumes recvfrom uses same type */243#ifdef FD_SETSIZE244fd_set readfds;245fd_set mask;246#else247int readfds;248int mask;249#endif /* def FD_SETSIZE */250struct sockaddr_in from;251struct rpc_msg reply_msg;252XDR reply_xdrs;253struct timeval time_waited, seltimeout;254bool_t ok;255int nrefreshes = 2; /* number of times to refresh cred */256struct timeval timeout;257long procl = proc;258259if (cu->cu_total.tv_usec == -1) {260timeout = utimeout; /* use supplied timeout */261} else {262timeout = cu->cu_total; /* use default timeout */263}264265time_waited.tv_sec = 0;266time_waited.tv_usec = 0;267call_again:268xdrs = &(cu->cu_outxdrs);269xdrs->x_op = XDR_ENCODE;270XDR_SETPOS(xdrs, cu->cu_xdrpos);271/*272* the transaction is the first thing in the out buffer273*/274(*(uint32_t *)(void *)(cu->cu_outbuf))++;275if ((! XDR_PUTLONG(xdrs, &procl)) ||276(! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||277(! AUTH_WRAP(cl->cl_auth, xdrs, xargs, argsp)))278return (cu->cu_error.re_status = RPC_CANTENCODEARGS);279outlen = (int)XDR_GETPOS(xdrs);280281send_again:282if (send(cu->cu_sock, cu->cu_outbuf, (u_int)outlen, 0) != outlen) {283cu->cu_error.re_errno = errno;284return (cu->cu_error.re_status = RPC_CANTSEND);285}286287/*288* Hack to provide rpc-based message passing289*/290if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {291return (cu->cu_error.re_status = RPC_TIMEDOUT);292}293/*294* sub-optimal code appears here because we have295* some clock time to spare while the packets are in flight.296* (We assume that this is actually only executed once.)297*/298reply_msg.acpted_rply.ar_verf = gssrpc__null_auth;299reply_msg.acpted_rply.ar_results.where = NULL;300reply_msg.acpted_rply.ar_results.proc = xdr_void;301#ifdef FD_SETSIZE302FD_ZERO(&mask);303FD_SET(cu->cu_sock, &mask);304#else305mask = 1 << cu->cu_sock;306#endif /* def FD_SETSIZE */307for (;;) {308readfds = mask;309seltimeout = cu->cu_wait;310switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set *)NULL,311(fd_set *)NULL, &seltimeout)) {312313case 0:314time_waited.tv_sec += cu->cu_wait.tv_sec;315time_waited.tv_usec += cu->cu_wait.tv_usec;316while (time_waited.tv_usec >= 1000000) {317time_waited.tv_sec++;318time_waited.tv_usec -= 1000000;319}320if ((time_waited.tv_sec < timeout.tv_sec) ||321((time_waited.tv_sec == timeout.tv_sec) &&322(time_waited.tv_usec < timeout.tv_usec)))323goto send_again;324return (cu->cu_error.re_status = RPC_TIMEDOUT);325326/*327* buggy in other cases because time_waited is not being328* updated.329*/330case -1:331if (errno == EINTR)332continue;333cu->cu_error.re_errno = errno;334return (cu->cu_error.re_status = RPC_CANTRECV);335}336do {337fromlen = sizeof(struct sockaddr);338inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,339cu->cu_recvsz, 0,340(struct sockaddr *)&from, &fromlen);341} while (inlen < 0 && errno == EINTR);342if (inlen < 0) {343if (errno == EWOULDBLOCK)344continue;345cu->cu_error.re_errno = errno;346return (cu->cu_error.re_status = RPC_CANTRECV);347}348if ((size_t)inlen < sizeof(uint32_t))349continue;350/* see if reply transaction id matches sent id */351if (*((uint32_t *)(void *)(cu->cu_inbuf)) !=352*((uint32_t *)(void *)(cu->cu_outbuf)))353continue;354/* we now assume we have the proper reply */355break;356}357358/*359* now decode and validate the response360*/361xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);362ok = xdr_replymsg(&reply_xdrs, &reply_msg);363/* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */364if (ok) {365gssrpc__seterr_reply(&reply_msg, &(cu->cu_error));366if (cu->cu_error.re_status == RPC_SUCCESS) {367if (! AUTH_VALIDATE(cl->cl_auth,368&reply_msg.acpted_rply.ar_verf)) {369cu->cu_error.re_status = RPC_AUTHERROR;370cu->cu_error.re_why = AUTH_INVALIDRESP;371} else if (! AUTH_UNWRAP(cl->cl_auth, &reply_xdrs,372xresults, resultsp)) {373if (cu->cu_error.re_status == RPC_SUCCESS)374cu->cu_error.re_status = RPC_CANTDECODERES;375}376} /* end successful completion */377else {378/* maybe our credentials need to be refreshed ... */379if (nrefreshes > 0 &&380AUTH_REFRESH(cl->cl_auth, &reply_msg)) {381nrefreshes--;382goto call_again;383}384} /* end of unsuccessful completion */385/* free verifier */386if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&387(reply_msg.acpted_rply.ar_verf.oa_base != NULL)) {388xdrs->x_op = XDR_FREE;389(void)xdr_opaque_auth(xdrs,390&(reply_msg.acpted_rply.ar_verf));391}392} /* end of valid reply message */393else {394/*395* It's possible for xdr_replymsg() to fail partway396* through its attempt to decode the result from the397* server. If this happens, it will leave the reply398* structure partially populated with dynamically399* allocated memory. (This can happen if someone uses400* clntudp_bufcreate() to create a CLIENT handle and401* specifies a receive buffer size that is too small.)402* This memory must be free()ed to avoid a leak.403*/404enum xdr_op op = reply_xdrs.x_op;405reply_xdrs.x_op = XDR_FREE;406xdr_replymsg(&reply_xdrs, &reply_msg);407reply_xdrs.x_op = op;408cu->cu_error.re_status = RPC_CANTDECODERES;409}410return (cu->cu_error.re_status);411}412413static void414clntudp_geterr(415CLIENT *cl,416struct rpc_err *errp)417{418struct cu_data *cu = (struct cu_data *)cl->cl_private;419420*errp = cu->cu_error;421}422423424static bool_t425clntudp_freeres(426CLIENT *cl,427xdrproc_t xdr_res,428void *res_ptr)429{430struct cu_data *cu = cl->cl_private;431XDR *xdrs = &cu->cu_outxdrs;432433xdrs->x_op = XDR_FREE;434return ((*xdr_res)(xdrs, res_ptr));435}436437438/*ARGSUSED*/439static void440clntudp_abort(CLIENT *h)441{442}443444static bool_t445clntudp_control(446CLIENT *cl,447int request,448void *info)449{450struct cu_data *cu = cl->cl_private;451452switch (request) {453case CLSET_TIMEOUT:454cu->cu_total = *(struct timeval *)info;455break;456case CLGET_TIMEOUT:457*(struct timeval *)info = cu->cu_total;458break;459case CLSET_RETRY_TIMEOUT:460cu->cu_wait = *(struct timeval *)info;461break;462case CLGET_RETRY_TIMEOUT:463*(struct timeval *)info = cu->cu_wait;464break;465case CLGET_SERVER_ADDR:466*(struct sockaddr_in *)info = cu->cu_raddr;467break;468case CLGET_LOCAL_ADDR:469*(struct sockaddr_in *)info = cu->cu_laddr;470break;471default:472return (FALSE);473}474return (TRUE);475}476477static void478clntudp_destroy(CLIENT *cl)479{480struct cu_data *cu = (struct cu_data *)cl->cl_private;481482if (cu->cu_closeit)483(void)closesocket(cu->cu_sock);484XDR_DESTROY(&(cu->cu_outxdrs));485mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));486mem_free((caddr_t)cl, sizeof(CLIENT));487}488489490