home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / comm / tcp / amitcp-sdk / src / rpclib / clnt_udp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-29  |  11.5 KB  |  448 lines

  1. /*
  2.  *      $Id: clnt_udp.c,v 4.2 1994/09/29 23:48:50 jraja Exp $
  3.  *
  4.  *      Implements a UDP/IP based, client side RPC.
  5.  *
  6.  *      Copyright © 1994 AmiTCP/IP Group,
  7.  *                       Network Solutions Development Inc.
  8.  *                       All rights reserved. 
  9.  */
  10.  
  11. /* @(#)clnt_udp.c    2.2 88/08/01 4.0 RPCSRC */
  12. #if !defined(lint) && defined(SCCSIDS)
  13. static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
  14. #endif
  15.  
  16. /*
  17.  * Copyright (C) 1984, Sun Microsystems, Inc.
  18.  */
  19.  
  20. #include <sys/param.h>
  21. #include <rpc/rpc.h>
  22. #include <sys/socket.h>
  23. #include <sys/ioctl.h>
  24. #include <netdb.h>
  25. #include <errno.h>
  26. #include <rpc/pmap_clnt.h>
  27. #include <stdio.h>
  28.  
  29. /*
  30.  * UDP bases client side rpc operations
  31.  */
  32. static enum clnt_stat    clntudp_call(CLIENT * h, u_long proc,
  33.                      xdrproc_t xdr_args, caddr_t args_ptr,
  34.                      xdrproc_t xdr_results,
  35.                      caddr_t results_ptr,
  36.                      struct timeval timeout);
  37. static void        clntudp_abort(CLIENT * h);
  38. static void        clntudp_geterr(CLIENT * h, struct rpc_err * errp);
  39. static bool_t        clntudp_freeres(CLIENT * cl, 
  40.                     xdrproc_t xdr_res, caddr_t res_ptr);
  41. static bool_t           clntudp_control(CLIENT * cl,
  42.                     u_int request, caddr_t info);
  43. static void        clntudp_destroy(CLIENT * h);
  44.  
  45. static struct clnt_ops udp_ops = {
  46.     clntudp_call,
  47.     clntudp_abort,
  48.     clntudp_geterr,
  49.     clntudp_freeres,
  50.     clntudp_destroy,
  51.     clntudp_control
  52. };
  53.  
  54. /* 
  55.  * Private data kept per client handle
  56.  */
  57. struct cu_data {
  58.     int           cu_sock;
  59.     bool_t           cu_closeit;
  60.     struct sockaddr_in cu_raddr;
  61.     int           cu_rlen;
  62.     struct timeval       cu_wait;
  63.     struct timeval     cu_total;
  64.     struct rpc_err       cu_error;
  65.     XDR           cu_outxdrs;
  66.     u_int           cu_xdrpos;
  67.     u_int           cu_sendsz;
  68.     char           *cu_outbuf;
  69.     u_int           cu_recvsz;
  70.     char           cu_inbuf[1];
  71. };
  72.  
  73. /*
  74.  * Create a UDP based client handle.
  75.  * If *sockp<0, *sockp is set to a newly created UPD socket.
  76.  * If raddr->sin_port is 0 a binder on the remote machine
  77.  * is consulted for the correct port number.
  78.  * NB: It is the clients responsibility to close *sockp.
  79.  * NB: The rpch->cl_auth is initialized to null authentication.
  80.  *     Caller may wish to set this something more useful.
  81.  *
  82.  * wait is the amount of time used between retransmitting a call if
  83.  * no response has been heard;  retransmition occurs until the actual
  84.  * rpc call times out.
  85.  *
  86.  * sendsz and recvsz are the maximum allowable packet sizes that can be
  87.  * sent and received.
  88.  */
  89. CLIENT *
  90. clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
  91.     struct sockaddr_in *raddr;
  92.     u_long program;
  93.     u_long version;
  94.     struct timeval wait;
  95.     register int *sockp;
  96.     u_int sendsz;
  97.     u_int recvsz;
  98. {
  99.     CLIENT *cl;
  100.     register struct cu_data *cu;
  101.     struct timeval now;
  102.     struct rpc_msg call_msg;
  103.  
  104.     cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
  105.     if (cl == NULL) {
  106.         (void) fprintf(stderr, "clntudp_create: out of memory\n");
  107.         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
  108.         rpc_createerr.cf_error.re_errno = errno;
  109.         goto fooy;
  110.     }
  111.     sendsz = ((sendsz + 3) / 4) * 4;
  112.     recvsz = ((recvsz + 3) / 4) * 4;
  113.     cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
  114.     if (cu == NULL) {
  115.         (void) fprintf(stderr, "clntudp_create: out of memory\n");
  116.         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
  117.         rpc_createerr.cf_error.re_errno = errno;
  118.         goto fooy;
  119.     }
  120.     cu->cu_outbuf = &cu->cu_inbuf[recvsz];
  121.  
  122.     (void)gettimeofday(&now, (struct timezone *)0);
  123.     if (raddr->sin_port == 0) {
  124.         u_short port;
  125.         if ((port =
  126.             pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
  127.             goto fooy;
  128.         }
  129.         raddr->sin_port = htons(port);
  130.     }
  131.     cl->cl_ops = &udp_ops;
  132.     cl->cl_private = (caddr_t)cu;
  133.     cu->cu_raddr = *raddr;
  134.     cu->cu_rlen = sizeof (cu->cu_raddr);
  135.     cu->cu_wait = wait;
  136.     cu->cu_total.tv_sec = -1;
  137.     cu->cu_total.tv_usec = -1;
  138.     cu->cu_sendsz = sendsz;
  139.     cu->cu_recvsz = recvsz;
  140.     call_msg.rm_xid = (u_long)getpid() ^ now.tv_sec ^ now.tv_usec;
  141.     call_msg.rm_direction = CALL;
  142.     call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
  143.     call_msg.rm_call.cb_prog = program;
  144.     call_msg.rm_call.cb_vers = version;
  145.     xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
  146.         sendsz, XDR_ENCODE);
  147.     if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
  148.         goto fooy;
  149.     }
  150.     cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
  151.     if (*sockp < 0) {
  152.         int dontblock = 1;
  153.  
  154.         *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  155.         if (*sockp < 0) {
  156.             rpc_createerr.cf_stat = RPC_SYSTEMERROR;
  157.             rpc_createerr.cf_error.re_errno = errno;
  158.             goto fooy;
  159.         }
  160.         /* attempt to bind to prov port */
  161.         (void)bindresvport(*sockp, (struct sockaddr_in *)0);
  162.         /* the sockets rpc controls are non-blocking */
  163. #ifdef AMITCP
  164.         (void)IoctlSocket(*sockp, FIONBIO, (char *) &dontblock);
  165. #else
  166.         (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
  167. #endif
  168.         cu->cu_closeit = TRUE;
  169.     } else {
  170.         cu->cu_closeit = FALSE;
  171.     }
  172.     cu->cu_sock = *sockp;
  173.     cl->cl_auth = authnone_create();
  174.     return (cl);
  175. fooy:
  176.     if (cu)
  177.         mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
  178.     if (cl)
  179.         mem_free((caddr_t)cl, sizeof(CLIENT));
  180.     return ((CLIENT *)NULL);
  181. }
  182.  
  183. CLIENT *
  184. clntudp_create(raddr, program, version, wait, sockp)
  185.     struct sockaddr_in *raddr;
  186.     u_long program;
  187.     u_long version;
  188.     struct timeval wait;
  189.     register int *sockp;
  190. {
  191.  
  192.     return(clntudp_bufcreate(raddr, program, version, wait, sockp,
  193.         UDPMSGSIZE, UDPMSGSIZE));
  194. }
  195.  
  196. static enum clnt_stat 
  197. clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
  198.     register CLIENT    *cl;        /* client handle */
  199.     u_long        proc;        /* procedure number */
  200.     xdrproc_t    xargs;        /* xdr routine for args */
  201.     caddr_t        argsp;        /* pointer to args */
  202.     xdrproc_t    xresults;    /* xdr routine for results */
  203.     caddr_t        resultsp;    /* pointer to results */
  204.     struct timeval    utimeout;    /* seconds to wait before giving up */
  205. {
  206.     register struct cu_data *cu = (struct cu_data *)cl->cl_private;
  207.     register XDR *xdrs;
  208.     register int outlen;
  209.     register int inlen;
  210. #ifdef AMITCP
  211.     long fromlen;
  212. #else
  213.     int fromlen;
  214. #endif
  215. #ifdef FD_SETSIZE
  216.     fd_set readfds;
  217.     fd_set mask;
  218. #else
  219.     int readfds;
  220.     register int mask;
  221. #endif /* def FD_SETSIZE */
  222.     struct sockaddr_in from;
  223.     struct rpc_msg reply_msg;
  224.     XDR reply_xdrs;
  225.     struct timeval time_waited;
  226.     bool_t ok;
  227.     int nrefreshes = 2;    /* number of times to refresh cred */
  228. #ifdef AMITCP
  229.     struct timeval timeout, t_temp;
  230. #else
  231.     struct timeval timeout;
  232. #endif
  233.     if (cu->cu_total.tv_usec == -1) {
  234.         timeout = utimeout;     /* use supplied timeout */
  235.     } else {
  236.         timeout = cu->cu_total; /* use default timeout */
  237.     }
  238.  
  239.     time_waited.tv_sec = 0;
  240.     time_waited.tv_usec = 0;
  241. call_again:
  242.     xdrs = &(cu->cu_outxdrs);
  243.     xdrs->x_op = XDR_ENCODE;
  244.     XDR_SETPOS(xdrs, cu->cu_xdrpos);
  245.     /*
  246.      * the transaction is the first thing in the out buffer
  247.      */
  248.     (*(u_short *)(cu->cu_outbuf))++;
  249.     if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
  250.         (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
  251.         (! (*xargs)(xdrs, argsp)))
  252.         return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
  253.     outlen = (int)XDR_GETPOS(xdrs);
  254.  
  255. send_again:
  256.     if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
  257.         (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
  258.         != outlen) {
  259.         cu->cu_error.re_errno = errno;
  260.         return (cu->cu_error.re_status = RPC_CANTSEND);
  261.     }
  262.  
  263.     /*
  264.      * Hack to provide rpc-based message passing
  265.      */
  266.     if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
  267.         return (cu->cu_error.re_status = RPC_TIMEDOUT);
  268.     }
  269.     /*
  270.      * sub-optimal code appears here because we have
  271.      * some clock time to spare while the packets are in flight.
  272.      * (We assume that this is actually only executed once.)
  273.      */
  274.     reply_msg.acpted_rply.ar_verf = _null_auth;
  275.     reply_msg.acpted_rply.ar_results.where = resultsp;
  276.     reply_msg.acpted_rply.ar_results.proc = xresults;
  277. #ifdef FD_SETSIZE
  278.     FD_ZERO(&mask);
  279.     FD_SET(cu->cu_sock, &mask);
  280. #else
  281.     mask = 1 << cu->cu_sock;
  282. #endif /* def FD_SETSIZE */
  283.     for (;;) {
  284.         readfds = mask;
  285.         switch (select(_rpc_dtablesize(), &readfds, NULL, 
  286.                    NULL, &(t_temp = cu->cu_wait))) {
  287.  
  288.         case 0:
  289.             time_waited.tv_sec += cu->cu_wait.tv_sec;
  290.             time_waited.tv_usec += cu->cu_wait.tv_usec;
  291.             while (time_waited.tv_usec >= 1000000) {
  292.                 time_waited.tv_sec++;
  293.                 time_waited.tv_usec -= 1000000;
  294.             }
  295.             if ((time_waited.tv_sec < timeout.tv_sec) ||
  296.                 ((time_waited.tv_sec == timeout.tv_sec) &&
  297.                 (time_waited.tv_usec < timeout.tv_usec)))
  298.                 goto send_again;    
  299.             return (cu->cu_error.re_status = RPC_TIMEDOUT);
  300.  
  301.         /*
  302.          * buggy in other cases because time_waited is not being
  303.          * updated.
  304.          */
  305.         case -1:
  306. #ifndef AMITCP /* EINTR is returned in case of a CTRL-C by default */
  307.             if (errno == EINTR)
  308.                 continue;    
  309. #endif
  310.             cu->cu_error.re_errno = errno;
  311.             return (cu->cu_error.re_status = RPC_CANTRECV);
  312.         }
  313. #ifndef AMITCP /* EINTR is returned in case of a CTRL-C by default */
  314.         do {
  315. #endif
  316.             fromlen = sizeof(struct sockaddr);
  317.             inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 
  318.                 (int) cu->cu_recvsz, 0,
  319.                 (struct sockaddr *)&from, &fromlen);
  320. #ifndef AMITCP /* EINTR is returned in case of a CTRL-C by default */
  321.         } while (inlen < 0 && errno == EINTR);
  322. #endif
  323.         if (inlen < 0) {
  324.             if (errno == EWOULDBLOCK)
  325.                 continue;    
  326.             cu->cu_error.re_errno = errno;
  327.             return (cu->cu_error.re_status = RPC_CANTRECV);
  328.         }
  329.         if (inlen < sizeof(u_long))
  330.             continue;    
  331.         /* see if reply transaction id matches sent id */
  332.         if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
  333.             continue;    
  334.         /* we now assume we have the proper reply */
  335.         break;
  336.     }
  337.  
  338.     /*
  339.      * now decode and validate the response
  340.      */
  341.     xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
  342.     ok = xdr_replymsg(&reply_xdrs, &reply_msg);
  343.     /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
  344.     if (ok) {
  345.         _seterr_reply(&reply_msg, &(cu->cu_error));
  346.         if (cu->cu_error.re_status == RPC_SUCCESS) {
  347.             if (! AUTH_VALIDATE(cl->cl_auth,
  348.                 &reply_msg.acpted_rply.ar_verf)) {
  349.                 cu->cu_error.re_status = RPC_AUTHERROR;
  350.                 cu->cu_error.re_why = AUTH_INVALIDRESP;
  351.             }
  352.             if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
  353.                 xdrs->x_op = XDR_FREE;
  354.                 (void)xdr_opaque_auth(xdrs,
  355.                     &(reply_msg.acpted_rply.ar_verf));
  356.             } 
  357.         }  /* end successful completion */
  358.         else {
  359.             /* maybe our credentials need to be refreshed ... */
  360.             if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
  361.                 nrefreshes--;
  362.                 goto call_again;
  363.             }
  364.         }  /* end of unsuccessful completion */
  365.     }  /* end of valid reply message */
  366.     else {
  367.         cu->cu_error.re_status = RPC_CANTDECODERES;
  368.     }
  369.     return (cu->cu_error.re_status);
  370. }
  371.  
  372. static void
  373. clntudp_geterr(cl, errp)
  374.     CLIENT *cl;
  375.     struct rpc_err *errp;
  376. {
  377.     register struct cu_data *cu = (struct cu_data *)cl->cl_private;
  378.  
  379.     *errp = cu->cu_error;
  380. }
  381.  
  382.  
  383. static bool_t
  384. clntudp_freeres(cl, xdr_res, res_ptr)
  385.     CLIENT *cl;
  386.     xdrproc_t xdr_res;
  387.     caddr_t res_ptr;
  388. {
  389.     register struct cu_data *cu = (struct cu_data *)cl->cl_private;
  390.     register XDR *xdrs = &(cu->cu_outxdrs);
  391.  
  392.     xdrs->x_op = XDR_FREE;
  393.     return ((*xdr_res)(xdrs, res_ptr));
  394. }
  395.  
  396. static void 
  397. clntudp_abort(CLIENT *h)
  398. {
  399. }
  400.  
  401. static bool_t
  402. clntudp_control(cl, request, info)
  403.     CLIENT *cl;
  404.     u_int request;
  405.     char *info;
  406. {
  407.     register struct cu_data *cu = (struct cu_data *)cl->cl_private;
  408.  
  409.     switch (request) {
  410.     case CLSET_TIMEOUT:
  411.         cu->cu_total = *(struct timeval *)info;
  412.         break;
  413.     case CLGET_TIMEOUT:
  414.         *(struct timeval *)info = cu->cu_total;
  415.         break;
  416.     case CLSET_RETRY_TIMEOUT:
  417.         cu->cu_wait = *(struct timeval *)info;
  418.         break;
  419.     case CLGET_RETRY_TIMEOUT:
  420.         *(struct timeval *)info = cu->cu_wait;
  421.         break;
  422.     case CLGET_SERVER_ADDR:
  423.         *(struct sockaddr_in *)info = cu->cu_raddr;
  424.         break;
  425.     default:
  426.         return (FALSE);
  427.     }
  428.     return (TRUE);
  429. }
  430.     
  431. static void
  432. clntudp_destroy(cl)
  433.     CLIENT *cl;
  434. {
  435.     register struct cu_data *cu = (struct cu_data *)cl->cl_private;
  436.  
  437.     if (cu->cu_closeit) {
  438. #ifdef AMITCP
  439.         (void)CloseSocket(cu->cu_sock);
  440. #else
  441.         (void)close(cu->cu_sock);
  442. #endif
  443.     }
  444.     XDR_DESTROY(&(cu->cu_outxdrs));
  445.     mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
  446.     mem_free((caddr_t)cl, sizeof(CLIENT));
  447. }
  448.