Path: blob/main/crypto/krb5/src/lib/rpc/clnt_tcp.c
39536 views
/* @(#)clnt_tcp.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_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";35#endif3637/*38* clnt_tcp.c, Implements a TCP/IP based, client side RPC.39*40* TCP based RPC supports 'batched calls'.41* A sequence of calls may be batched-up in a send buffer. The rpc call42* return immediately to the client even though the call was not necessarily43* sent. The batching occurs if the results' xdr routine is NULL (0) AND44* the rpc timeout value is zero (see clnt.h, rpc).45*46* Clients should NOT casually batch calls that in fact return results; that is,47* the server side should be aware that a call is batched and not produce any48* return message. Batched calls that produce many result messages can49* deadlock (netlock) the client and the server....50*51* Now go hang yourself.52*/5354#include <stdio.h>55#include <unistd.h>56#include <gssrpc/rpc.h>57#include <sys/socket.h>58#include <netdb.h>59#include <errno.h>60#include <string.h>61#include <gssrpc/pmap_clnt.h>62/* FD_ZERO may need memset declaration (e.g., Solaris 9) */63#include <string.h>64#include <port-sockets.h>6566#define MCALL_MSG_SIZE 246768#ifndef GETSOCKNAME_ARG3_TYPE69#define GETSOCKNAME_ARG3_TYPE int70#endif7172static enum clnt_stat clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void *,73xdrproc_t, void *, struct timeval);74static void clnttcp_abort(CLIENT *);75static void clnttcp_geterr(CLIENT *, struct rpc_err *);76static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *);77static bool_t clnttcp_control(CLIENT *, int, void *);78static void clnttcp_destroy(CLIENT *);7980static struct clnt_ops tcp_ops = {81clnttcp_call,82clnttcp_abort,83clnttcp_geterr,84clnttcp_freeres,85clnttcp_destroy,86clnttcp_control87};8889struct ct_data {90int ct_sock;91bool_t ct_closeit;92struct timeval ct_wait;93bool_t ct_waitset; /* wait set by clnt_control? */94struct sockaddr_in ct_addr;95struct rpc_err ct_error;96union {97char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */98uint32_t ct_mcalli;99} ct_u;100u_int ct_mpos; /* pos after marshal */101XDR ct_xdrs;102};103104static int readtcp(char *, caddr_t, int);105static int writetcp(char *, caddr_t, int);106107108/*109* Create a client handle for a tcp/ip connection.110* If *sockp<0, *sockp is set to a newly created TCP socket and it is111* connected to raddr. If *sockp non-negative then112* raddr is ignored. The rpc/tcp package does buffering113* similar to stdio, so the client must pick send and receive buffer sizes,];114* 0 => use the default.115* If raddr->sin_port is 0, then a binder on the remote machine is116* consulted for the right port number.117* NB: *sockp is copied into a private area.118* NB: It is the clients responsibility to close *sockp.119* NB: The rpch->cl_auth is set null authentication. Caller may wish to set this120* something more useful.121*/122CLIENT *123clnttcp_create(124struct sockaddr_in *raddr,125rpcprog_t prog,126rpcvers_t vers,127SOCKET *sockp,128u_int sendsz,129u_int recvsz)130{131CLIENT *h;132struct ct_data *ct = 0;133struct timeval now;134struct rpc_msg call_msg;135136h = (CLIENT *)mem_alloc(sizeof(*h));137if (h == NULL) {138(void)fprintf(stderr, "clnttcp_create: out of memory\n");139rpc_createerr.cf_stat = RPC_SYSTEMERROR;140rpc_createerr.cf_error.re_errno = errno;141goto fooy;142}143ct = (struct ct_data *)mem_alloc(sizeof(*ct));144if (ct == NULL) {145(void)fprintf(stderr, "clnttcp_create: out of memory\n");146rpc_createerr.cf_stat = RPC_SYSTEMERROR;147rpc_createerr.cf_error.re_errno = errno;148goto fooy;149}150151/*152* If no port number given ask the pmap for one153*/154if (raddr != NULL && raddr->sin_port == 0) {155u_short port;156if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {157mem_free((caddr_t)ct, sizeof(struct ct_data));158mem_free((caddr_t)h, sizeof(CLIENT));159return ((CLIENT *)NULL);160}161raddr->sin_port = htons(port);162}163164/*165* If no socket given, open one166*/167if (*sockp < 0) {168*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);169(void)bindresvport_sa(*sockp, NULL);170if (*sockp < 0 || raddr == NULL ||171connect(*sockp, (struct sockaddr *)raddr,172sizeof(*raddr)) < 0) {173rpc_createerr.cf_stat = RPC_SYSTEMERROR;174rpc_createerr.cf_error.re_errno = errno;175(void)closesocket(*sockp);176goto fooy;177}178ct->ct_closeit = TRUE;179} else {180ct->ct_closeit = FALSE;181}182183/*184* Set up private data struct185*/186ct->ct_sock = *sockp;187ct->ct_wait.tv_usec = 0;188ct->ct_waitset = FALSE;189if (raddr == NULL) {190/* Get the remote address from the socket, if it's IPv4. */191struct sockaddr_in sin;192socklen_t len = sizeof(sin);193int ret = getpeername(ct->ct_sock, (struct sockaddr *)&sin, &len);194if (ret == 0 && len == sizeof(sin) && sin.sin_family == AF_INET)195ct->ct_addr = sin;196else197memset(&ct->ct_addr, 0, sizeof(ct->ct_addr));198} else199ct->ct_addr = *raddr;200201/*202* Initialize call message203*/204(void)gettimeofday(&now, (struct timezone *)0);205call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;206call_msg.rm_direction = CALL;207call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;208call_msg.rm_call.cb_prog = prog;209call_msg.rm_call.cb_vers = vers;210211/*212* pre-serialize the staic part of the call msg and stash it away213*/214xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcall, MCALL_MSG_SIZE,215XDR_ENCODE);216if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {217if (ct->ct_closeit)218(void)closesocket(*sockp);219goto fooy;220}221ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));222XDR_DESTROY(&(ct->ct_xdrs));223224/*225* Create a client handle which uses xdrrec for serialization226* and authnone for authentication.227*/228xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,229(caddr_t)ct, readtcp, writetcp);230h->cl_ops = &tcp_ops;231h->cl_private = (caddr_t) ct;232h->cl_auth = authnone_create();233return (h);234235fooy:236/*237* Something goofed, free stuff and barf238*/239mem_free((caddr_t)ct, sizeof(struct ct_data));240mem_free((caddr_t)h, sizeof(CLIENT));241return ((CLIENT *)NULL);242}243244static enum clnt_stat245clnttcp_call(246CLIENT *h,247rpcproc_t proc,248xdrproc_t xdr_args,249void * args_ptr,250xdrproc_t xdr_results,251void * results_ptr,252struct timeval timeout)253{254struct ct_data *ct = h->cl_private;255XDR *xdrs = &ct->ct_xdrs;256struct rpc_msg reply_msg;257uint32_t x_id;258uint32_t *msg_x_id = &ct->ct_u.ct_mcalli; /* yuk */259bool_t shipnow;260int refreshes = 2;261long procl = proc;262263if (!ct->ct_waitset) {264ct->ct_wait = timeout;265}266267shipnow =268(xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0269&& timeout.tv_usec == 0) ? FALSE : TRUE;270271call_again:272xdrs->x_op = XDR_ENCODE;273ct->ct_error.re_status = RPC_SUCCESS;274x_id = ntohl(--(*msg_x_id));275if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcall, ct->ct_mpos)) ||276(! XDR_PUTLONG(xdrs, &procl)) ||277(! AUTH_MARSHALL(h->cl_auth, xdrs)) ||278(! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) {279if (ct->ct_error.re_status == RPC_SUCCESS)280ct->ct_error.re_status = RPC_CANTENCODEARGS;281(void)xdrrec_endofrecord(xdrs, TRUE);282return (ct->ct_error.re_status);283}284if (! xdrrec_endofrecord(xdrs, shipnow))285return (ct->ct_error.re_status = RPC_CANTSEND);286if (! shipnow)287return (RPC_SUCCESS);288/*289* Hack to provide rpc-based message passing290*/291if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {292return(ct->ct_error.re_status = RPC_TIMEDOUT);293}294295296/*297* Keep receiving until we get a valid transaction id298*/299xdrs->x_op = XDR_DECODE;300while (TRUE) {301reply_msg.acpted_rply.ar_verf = gssrpc__null_auth;302reply_msg.acpted_rply.ar_results.where = NULL;303reply_msg.acpted_rply.ar_results.proc = xdr_void;304if (! xdrrec_skiprecord(xdrs))305return (ct->ct_error.re_status);306/* now decode and validate the response header */307if (! xdr_replymsg(xdrs, &reply_msg)) {308/*309* Free some stuff allocated by xdr_replymsg()310* to avoid leaks, since it may allocate311* memory from partially successful decodes.312*/313enum xdr_op op = xdrs->x_op;314xdrs->x_op = XDR_FREE;315xdr_replymsg(xdrs, &reply_msg);316xdrs->x_op = op;317if (ct->ct_error.re_status == RPC_SUCCESS)318continue;319return (ct->ct_error.re_status);320}321if (reply_msg.rm_xid == x_id)322break;323}324325/*326* process header327*/328gssrpc__seterr_reply(&reply_msg, &(ct->ct_error));329if (ct->ct_error.re_status == RPC_SUCCESS) {330if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {331ct->ct_error.re_status = RPC_AUTHERROR;332ct->ct_error.re_why = AUTH_INVALIDRESP;333} else if (! AUTH_UNWRAP(h->cl_auth, xdrs,334xdr_results, results_ptr)) {335if (ct->ct_error.re_status == RPC_SUCCESS)336ct->ct_error.re_status = RPC_CANTDECODERES;337}338} /* end successful completion */339else {340/* maybe our credentials need to be refreshed ... */341if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))342goto call_again;343} /* end of unsuccessful completion */344/* free verifier ... */345if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&346(reply_msg.acpted_rply.ar_verf.oa_base != NULL)) {347xdrs->x_op = XDR_FREE;348(void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));349}350return (ct->ct_error.re_status);351}352353static void354clnttcp_geterr(355CLIENT *h,356struct rpc_err *errp)357{358struct ct_data *ct = h->cl_private;359360*errp = ct->ct_error;361}362363static bool_t364clnttcp_freeres(365CLIENT *cl,366xdrproc_t xdr_res,367void * res_ptr)368{369struct ct_data *ct = cl->cl_private;370XDR *xdrs = &ct->ct_xdrs;371372xdrs->x_op = XDR_FREE;373return ((*xdr_res)(xdrs, res_ptr));374}375376/*ARGSUSED*/377static void378clnttcp_abort(CLIENT *cl)379{380}381382static bool_t383clnttcp_control(384CLIENT *cl,385int request,386void *info)387{388struct ct_data *ct = cl->cl_private;389GETSOCKNAME_ARG3_TYPE len;390391switch (request) {392case CLSET_TIMEOUT:393ct->ct_wait = *(struct timeval *)info;394ct->ct_waitset = TRUE;395break;396case CLGET_TIMEOUT:397*(struct timeval *)info = ct->ct_wait;398break;399case CLGET_SERVER_ADDR:400*(struct sockaddr_in *)info = ct->ct_addr;401break;402case CLGET_LOCAL_ADDR:403len = sizeof(struct sockaddr);404if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0)405return FALSE;406else407return TRUE;408default:409return (FALSE);410}411return (TRUE);412}413414415static void416clnttcp_destroy(CLIENT *h)417{418struct ct_data *ct = h->cl_private;419420if (ct->ct_closeit)421(void)closesocket(ct->ct_sock);422XDR_DESTROY(&(ct->ct_xdrs));423mem_free((caddr_t)ct, sizeof(struct ct_data));424mem_free((caddr_t)h, sizeof(CLIENT));425}426427/*428* Interface between xdr serializer and tcp connection.429* Behaves like the system calls, read & write, but keeps some error state430* around for the rpc level.431*/432static int433readtcp(434char *ctptr,435caddr_t buf,436int len)437{438struct ct_data *ct = (void *)ctptr;439struct timeval tout;440#ifdef FD_SETSIZE441fd_set mask;442fd_set readfds;443444if (len == 0)445return (0);446FD_ZERO(&mask);447FD_SET(ct->ct_sock, &mask);448#else449int mask = 1 << (ct->ct_sock);450int readfds;451452if (len == 0)453return (0);454455#endif /* def FD_SETSIZE */456while (TRUE) {457readfds = mask;458tout = ct->ct_wait;459switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL,460&tout)) {461case 0:462ct->ct_error.re_status = RPC_TIMEDOUT;463return (-1);464465case -1:466if (errno == EINTR)467continue;468ct->ct_error.re_status = RPC_CANTRECV;469ct->ct_error.re_errno = errno;470return (-1);471}472break;473}474switch (len = read(ct->ct_sock, buf, (size_t) len)) {475476case 0:477/* premature eof */478ct->ct_error.re_errno = ECONNRESET;479ct->ct_error.re_status = RPC_CANTRECV;480len = -1; /* it's really an error */481break;482483case -1:484ct->ct_error.re_errno = errno;485ct->ct_error.re_status = RPC_CANTRECV;486break;487}488return (len);489}490491static int492writetcp(493char *ctptr,494caddr_t buf,495int len)496{497struct ct_data *ct = (struct ct_data *)(void *)ctptr;498int i, cnt;499500for (cnt = len; cnt > 0; cnt -= i, buf += i) {501if ((i = write(ct->ct_sock, buf, (size_t) cnt)) == -1) {502ct->ct_error.re_errno = errno;503ct->ct_error.re_status = RPC_CANTSEND;504return (-1);505}506}507return (len);508}509510511