145094Smckusick /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ 245094Smckusick /* 345094Smckusick * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 445094Smckusick * unrestricted use provided that this legend is included on all tape 545094Smckusick * media and as a part of the software program in whole or part. Users 645094Smckusick * may copy or modify Sun RPC without charge, but are not authorized 745094Smckusick * to license or distribute it to anyone else except as part of a product or 845094Smckusick * program developed by the user. 945094Smckusick * 1045094Smckusick * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 1145094Smckusick * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 1245094Smckusick * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 1345094Smckusick * 1445094Smckusick * Sun RPC is provided with no support and without any obligation on the 1545094Smckusick * part of Sun Microsystems, Inc. to assist in its use, correction, 1645094Smckusick * modification or enhancement. 1745094Smckusick * 1845094Smckusick * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 1945094Smckusick * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 2045094Smckusick * OR ANY PART THEREOF. 2145094Smckusick * 2245094Smckusick * In no event will Sun Microsystems, Inc. be liable for any lost revenue 2345094Smckusick * or profits or other special, indirect and consequential damages, even if 2445094Smckusick * Sun has been advised of the possibility of such damages. 2545094Smckusick * 2645094Smckusick * Sun Microsystems, Inc. 2745094Smckusick * 2550 Garcia Avenue 2845094Smckusick * Mountain View, California 94043 2945094Smckusick */ 3045094Smckusick #if !defined(lint) && defined(SCCSIDS) 3145094Smckusick static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; 3245094Smckusick #endif 3345094Smckusick 3445094Smckusick /* 3545094Smckusick * svc_udp.c, 3645094Smckusick * Server side for UDP/IP based RPC. (Does some caching in the hopes of 3745094Smckusick * achieving execute-at-most-once semantics.) 3845094Smckusick * 3945094Smckusick * Copyright (C) 1984, Sun Microsystems, Inc. 4045094Smckusick */ 4145094Smckusick 4245094Smckusick #include <stdio.h> 4345094Smckusick #include <rpc/rpc.h> 4445094Smckusick #include <sys/socket.h> 4545094Smckusick #include <errno.h> 4645094Smckusick 4745094Smckusick 4845094Smckusick #define rpc_buffer(xprt) ((xprt)->xp_p1) 4945094Smckusick #define MAX(a, b) ((a > b) ? a : b) 5045094Smckusick 5145094Smckusick static bool_t svcudp_recv(); 5245094Smckusick static bool_t svcudp_reply(); 5345094Smckusick static enum xprt_stat svcudp_stat(); 5445094Smckusick static bool_t svcudp_getargs(); 5545094Smckusick static bool_t svcudp_freeargs(); 5645094Smckusick static void svcudp_destroy(); 5745094Smckusick 5845094Smckusick static struct xp_ops svcudp_op = { 5945094Smckusick svcudp_recv, 6045094Smckusick svcudp_stat, 6145094Smckusick svcudp_getargs, 6245094Smckusick svcudp_reply, 6345094Smckusick svcudp_freeargs, 6445094Smckusick svcudp_destroy 6545094Smckusick }; 6645094Smckusick 6745094Smckusick extern int errno; 6845094Smckusick 6945094Smckusick /* 7045094Smckusick * kept in xprt->xp_p2 7145094Smckusick */ 7245094Smckusick struct svcudp_data { 7345094Smckusick u_int su_iosz; /* byte size of send.recv buffer */ 7445094Smckusick u_long su_xid; /* transaction id */ 7545094Smckusick XDR su_xdrs; /* XDR handle */ 7645094Smckusick char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ 7745094Smckusick char * su_cache; /* cached data, NULL if no cache */ 7845094Smckusick }; 7945094Smckusick #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) 8045094Smckusick 8145094Smckusick /* 8245094Smckusick * Usage: 8345094Smckusick * xprt = svcudp_create(sock); 8445094Smckusick * 8545094Smckusick * If sock<0 then a socket is created, else sock is used. 8645094Smckusick * If the socket, sock is not bound to a port then svcudp_create 8745094Smckusick * binds it to an arbitrary port. In any (successful) case, 8845094Smckusick * xprt->xp_sock is the registered socket number and xprt->xp_port is the 8945094Smckusick * associated port number. 9045094Smckusick * Once *xprt is initialized, it is registered as a transporter; 9145094Smckusick * see (svc.h, xprt_register). 9245094Smckusick * The routines returns NULL if a problem occurred. 9345094Smckusick */ 9445094Smckusick SVCXPRT * 9545094Smckusick svcudp_bufcreate(sock, sendsz, recvsz) 9645094Smckusick register int sock; 9745094Smckusick u_int sendsz, recvsz; 9845094Smckusick { 9945094Smckusick bool_t madesock = FALSE; 10045094Smckusick register SVCXPRT *xprt; 10145094Smckusick register struct svcudp_data *su; 10245094Smckusick struct sockaddr_in addr; 10345094Smckusick int len = sizeof(struct sockaddr_in); 10445094Smckusick 10545094Smckusick if (sock == RPC_ANYSOCK) { 10645094Smckusick if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 10745094Smckusick perror("svcudp_create: socket creation problem"); 10845094Smckusick return ((SVCXPRT *)NULL); 10945094Smckusick } 11045094Smckusick madesock = TRUE; 11145094Smckusick } 11245094Smckusick bzero((char *)&addr, sizeof (addr)); 11345094Smckusick addr.sin_family = AF_INET; 11445094Smckusick if (bindresvport(sock, &addr)) { 11545094Smckusick addr.sin_port = 0; 11645094Smckusick (void)bind(sock, (struct sockaddr *)&addr, len); 11745094Smckusick } 11845094Smckusick if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { 11945094Smckusick perror("svcudp_create - cannot getsockname"); 12045094Smckusick if (madesock) 12145094Smckusick (void)close(sock); 12245094Smckusick return ((SVCXPRT *)NULL); 12345094Smckusick } 12445094Smckusick xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); 12545094Smckusick if (xprt == NULL) { 12645094Smckusick (void)fprintf(stderr, "svcudp_create: out of memory\n"); 12745094Smckusick return (NULL); 12845094Smckusick } 12945094Smckusick su = (struct svcudp_data *)mem_alloc(sizeof(*su)); 13045094Smckusick if (su == NULL) { 13145094Smckusick (void)fprintf(stderr, "svcudp_create: out of memory\n"); 13245094Smckusick return (NULL); 13345094Smckusick } 13445094Smckusick su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 13545094Smckusick if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) { 13645094Smckusick (void)fprintf(stderr, "svcudp_create: out of memory\n"); 13745094Smckusick return (NULL); 13845094Smckusick } 13945094Smckusick xdrmem_create( 14045094Smckusick &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); 14145094Smckusick su->su_cache = NULL; 14245094Smckusick xprt->xp_p2 = (caddr_t)su; 14345094Smckusick xprt->xp_verf.oa_base = su->su_verfbody; 14445094Smckusick xprt->xp_ops = &svcudp_op; 14545094Smckusick xprt->xp_port = ntohs(addr.sin_port); 14645094Smckusick xprt->xp_sock = sock; 14745094Smckusick xprt_register(xprt); 14845094Smckusick return (xprt); 14945094Smckusick } 15045094Smckusick 15145094Smckusick SVCXPRT * 15245094Smckusick svcudp_create(sock) 15345094Smckusick int sock; 15445094Smckusick { 15545094Smckusick 15645094Smckusick return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 15745094Smckusick } 15845094Smckusick 15945094Smckusick static enum xprt_stat 16045094Smckusick svcudp_stat(xprt) 16145094Smckusick SVCXPRT *xprt; 16245094Smckusick { 16345094Smckusick 16445094Smckusick return (XPRT_IDLE); 16545094Smckusick } 16645094Smckusick 16745094Smckusick static bool_t 16845094Smckusick svcudp_recv(xprt, msg) 16945094Smckusick register SVCXPRT *xprt; 17045094Smckusick struct rpc_msg *msg; 17145094Smckusick { 17245094Smckusick register struct svcudp_data *su = su_data(xprt); 17345094Smckusick register XDR *xdrs = &(su->su_xdrs); 17445094Smckusick register int rlen; 17545094Smckusick char *reply; 17645094Smckusick u_long replylen; 177*46639Sbostic static int cache_get(); 17845094Smckusick 17945094Smckusick again: 18045094Smckusick xprt->xp_addrlen = sizeof(struct sockaddr_in); 18145094Smckusick rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 18245094Smckusick 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 18345094Smckusick if (rlen == -1 && errno == EINTR) 18445094Smckusick goto again; 18545094Smckusick if (rlen < 4*sizeof(u_long)) 18645094Smckusick return (FALSE); 18745094Smckusick xdrs->x_op = XDR_DECODE; 18845094Smckusick XDR_SETPOS(xdrs, 0); 18945094Smckusick if (! xdr_callmsg(xdrs, msg)) 19045094Smckusick return (FALSE); 19145094Smckusick su->su_xid = msg->rm_xid; 19245094Smckusick if (su->su_cache != NULL) { 19345094Smckusick if (cache_get(xprt, msg, &reply, &replylen)) { 19445094Smckusick (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 19545094Smckusick (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); 19645094Smckusick return (TRUE); 19745094Smckusick } 19845094Smckusick } 19945094Smckusick return (TRUE); 20045094Smckusick } 20145094Smckusick 20245094Smckusick static bool_t 20345094Smckusick svcudp_reply(xprt, msg) 20445094Smckusick register SVCXPRT *xprt; 20545094Smckusick struct rpc_msg *msg; 20645094Smckusick { 20745094Smckusick register struct svcudp_data *su = su_data(xprt); 20845094Smckusick register XDR *xdrs = &(su->su_xdrs); 20945094Smckusick register int slen; 21045094Smckusick register bool_t stat = FALSE; 211*46639Sbostic static void cache_set(); 21245094Smckusick 21345094Smckusick xdrs->x_op = XDR_ENCODE; 21445094Smckusick XDR_SETPOS(xdrs, 0); 21545094Smckusick msg->rm_xid = su->su_xid; 21645094Smckusick if (xdr_replymsg(xdrs, msg)) { 21745094Smckusick slen = (int)XDR_GETPOS(xdrs); 21845094Smckusick if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 21945094Smckusick (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 22045094Smckusick == slen) { 22145094Smckusick stat = TRUE; 22245094Smckusick if (su->su_cache && slen >= 0) { 22345094Smckusick cache_set(xprt, (u_long) slen); 22445094Smckusick } 22545094Smckusick } 22645094Smckusick } 22745094Smckusick return (stat); 22845094Smckusick } 22945094Smckusick 23045094Smckusick static bool_t 23145094Smckusick svcudp_getargs(xprt, xdr_args, args_ptr) 23245094Smckusick SVCXPRT *xprt; 23345094Smckusick xdrproc_t xdr_args; 23445094Smckusick caddr_t args_ptr; 23545094Smckusick { 23645094Smckusick 23745094Smckusick return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 23845094Smckusick } 23945094Smckusick 24045094Smckusick static bool_t 24145094Smckusick svcudp_freeargs(xprt, xdr_args, args_ptr) 24245094Smckusick SVCXPRT *xprt; 24345094Smckusick xdrproc_t xdr_args; 24445094Smckusick caddr_t args_ptr; 24545094Smckusick { 24645094Smckusick register XDR *xdrs = &(su_data(xprt)->su_xdrs); 24745094Smckusick 24845094Smckusick xdrs->x_op = XDR_FREE; 24945094Smckusick return ((*xdr_args)(xdrs, args_ptr)); 25045094Smckusick } 25145094Smckusick 25245094Smckusick static void 25345094Smckusick svcudp_destroy(xprt) 25445094Smckusick register SVCXPRT *xprt; 25545094Smckusick { 25645094Smckusick register struct svcudp_data *su = su_data(xprt); 25745094Smckusick 25845094Smckusick xprt_unregister(xprt); 25945094Smckusick (void)close(xprt->xp_sock); 26045094Smckusick XDR_DESTROY(&(su->su_xdrs)); 26145094Smckusick mem_free(rpc_buffer(xprt), su->su_iosz); 26245094Smckusick mem_free((caddr_t)su, sizeof(struct svcudp_data)); 26345094Smckusick mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 26445094Smckusick } 26545094Smckusick 26645094Smckusick 26745094Smckusick /***********this could be a separate file*********************/ 26845094Smckusick 26945094Smckusick /* 27045094Smckusick * Fifo cache for udp server 27145094Smckusick * Copies pointers to reply buffers into fifo cache 27245094Smckusick * Buffers are sent again if retransmissions are detected. 27345094Smckusick */ 27445094Smckusick 27545094Smckusick #define SPARSENESS 4 /* 75% sparse */ 27645094Smckusick 27745094Smckusick #define CACHE_PERROR(msg) \ 27845094Smckusick (void) fprintf(stderr,"%s\n", msg) 27945094Smckusick 28045094Smckusick #define ALLOC(type, size) \ 28145094Smckusick (type *) mem_alloc((unsigned) (sizeof(type) * (size))) 28245094Smckusick 28345094Smckusick #define BZERO(addr, type, size) \ 28445094Smckusick bzero((char *) addr, sizeof(type) * (int) (size)) 28545094Smckusick 28645094Smckusick /* 28745094Smckusick * An entry in the cache 28845094Smckusick */ 28945094Smckusick typedef struct cache_node *cache_ptr; 29045094Smckusick struct cache_node { 29145094Smckusick /* 29245094Smckusick * Index into cache is xid, proc, vers, prog and address 29345094Smckusick */ 29445094Smckusick u_long cache_xid; 29545094Smckusick u_long cache_proc; 29645094Smckusick u_long cache_vers; 29745094Smckusick u_long cache_prog; 29845094Smckusick struct sockaddr_in cache_addr; 29945094Smckusick /* 30045094Smckusick * The cached reply and length 30145094Smckusick */ 30245094Smckusick char * cache_reply; 30345094Smckusick u_long cache_replylen; 30445094Smckusick /* 30545094Smckusick * Next node on the list, if there is a collision 30645094Smckusick */ 30745094Smckusick cache_ptr cache_next; 30845094Smckusick }; 30945094Smckusick 31045094Smckusick 31145094Smckusick 31245094Smckusick /* 31345094Smckusick * The entire cache 31445094Smckusick */ 31545094Smckusick struct udp_cache { 31645094Smckusick u_long uc_size; /* size of cache */ 31745094Smckusick cache_ptr *uc_entries; /* hash table of entries in cache */ 31845094Smckusick cache_ptr *uc_fifo; /* fifo list of entries in cache */ 31945094Smckusick u_long uc_nextvictim; /* points to next victim in fifo list */ 32045094Smckusick u_long uc_prog; /* saved program number */ 32145094Smckusick u_long uc_vers; /* saved version number */ 32245094Smckusick u_long uc_proc; /* saved procedure number */ 32345094Smckusick struct sockaddr_in uc_addr; /* saved caller's address */ 32445094Smckusick }; 32545094Smckusick 32645094Smckusick 32745094Smckusick /* 32845094Smckusick * the hashing function 32945094Smckusick */ 33045094Smckusick #define CACHE_LOC(transp, xid) \ 33145094Smckusick (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 33245094Smckusick 33345094Smckusick 33445094Smckusick /* 33545094Smckusick * Enable use of the cache. 33645094Smckusick * Note: there is no disable. 33745094Smckusick */ 33845094Smckusick svcudp_enablecache(transp, size) 33945094Smckusick SVCXPRT *transp; 34045094Smckusick u_long size; 34145094Smckusick { 34245094Smckusick struct svcudp_data *su = su_data(transp); 34345094Smckusick struct udp_cache *uc; 34445094Smckusick 34545094Smckusick if (su->su_cache != NULL) { 34645094Smckusick CACHE_PERROR("enablecache: cache already enabled"); 34745094Smckusick return(0); 34845094Smckusick } 34945094Smckusick uc = ALLOC(struct udp_cache, 1); 35045094Smckusick if (uc == NULL) { 35145094Smckusick CACHE_PERROR("enablecache: could not allocate cache"); 35245094Smckusick return(0); 35345094Smckusick } 35445094Smckusick uc->uc_size = size; 35545094Smckusick uc->uc_nextvictim = 0; 35645094Smckusick uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 35745094Smckusick if (uc->uc_entries == NULL) { 35845094Smckusick CACHE_PERROR("enablecache: could not allocate cache data"); 35945094Smckusick return(0); 36045094Smckusick } 36145094Smckusick BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); 36245094Smckusick uc->uc_fifo = ALLOC(cache_ptr, size); 36345094Smckusick if (uc->uc_fifo == NULL) { 36445094Smckusick CACHE_PERROR("enablecache: could not allocate cache fifo"); 36545094Smckusick return(0); 36645094Smckusick } 36745094Smckusick BZERO(uc->uc_fifo, cache_ptr, size); 36845094Smckusick su->su_cache = (char *) uc; 36945094Smckusick return(1); 37045094Smckusick } 37145094Smckusick 37245094Smckusick 37345094Smckusick /* 37445094Smckusick * Set an entry in the cache 37545094Smckusick */ 376*46639Sbostic static void 37745094Smckusick cache_set(xprt, replylen) 37845094Smckusick SVCXPRT *xprt; 37945094Smckusick u_long replylen; 38045094Smckusick { 38145094Smckusick register cache_ptr victim; 38245094Smckusick register cache_ptr *vicp; 38345094Smckusick register struct svcudp_data *su = su_data(xprt); 38445094Smckusick struct udp_cache *uc = (struct udp_cache *) su->su_cache; 38545094Smckusick u_int loc; 38645094Smckusick char *newbuf; 38745094Smckusick 38845094Smckusick /* 38945094Smckusick * Find space for the new entry, either by 39045094Smckusick * reusing an old entry, or by mallocing a new one 39145094Smckusick */ 39245094Smckusick victim = uc->uc_fifo[uc->uc_nextvictim]; 39345094Smckusick if (victim != NULL) { 39445094Smckusick loc = CACHE_LOC(xprt, victim->cache_xid); 39545094Smckusick for (vicp = &uc->uc_entries[loc]; 39645094Smckusick *vicp != NULL && *vicp != victim; 39745094Smckusick vicp = &(*vicp)->cache_next) 39845094Smckusick ; 39945094Smckusick if (*vicp == NULL) { 40045094Smckusick CACHE_PERROR("cache_set: victim not found"); 40145094Smckusick return; 40245094Smckusick } 40345094Smckusick *vicp = victim->cache_next; /* remote from cache */ 40445094Smckusick newbuf = victim->cache_reply; 40545094Smckusick } else { 40645094Smckusick victim = ALLOC(struct cache_node, 1); 40745094Smckusick if (victim == NULL) { 40845094Smckusick CACHE_PERROR("cache_set: victim alloc failed"); 40945094Smckusick return; 41045094Smckusick } 41145094Smckusick newbuf = mem_alloc(su->su_iosz); 41245094Smckusick if (newbuf == NULL) { 41345094Smckusick CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); 41445094Smckusick return; 41545094Smckusick } 41645094Smckusick } 41745094Smckusick 41845094Smckusick /* 41945094Smckusick * Store it away 42045094Smckusick */ 42145094Smckusick victim->cache_replylen = replylen; 42245094Smckusick victim->cache_reply = rpc_buffer(xprt); 42345094Smckusick rpc_buffer(xprt) = newbuf; 42445094Smckusick xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 42545094Smckusick victim->cache_xid = su->su_xid; 42645094Smckusick victim->cache_proc = uc->uc_proc; 42745094Smckusick victim->cache_vers = uc->uc_vers; 42845094Smckusick victim->cache_prog = uc->uc_prog; 42945094Smckusick victim->cache_addr = uc->uc_addr; 43045094Smckusick loc = CACHE_LOC(xprt, victim->cache_xid); 43145094Smckusick victim->cache_next = uc->uc_entries[loc]; 43245094Smckusick uc->uc_entries[loc] = victim; 43345094Smckusick uc->uc_fifo[uc->uc_nextvictim++] = victim; 43445094Smckusick uc->uc_nextvictim %= uc->uc_size; 43545094Smckusick } 43645094Smckusick 43745094Smckusick /* 43845094Smckusick * Try to get an entry from the cache 43945094Smckusick * return 1 if found, 0 if not found 44045094Smckusick */ 44145094Smckusick static 44245094Smckusick cache_get(xprt, msg, replyp, replylenp) 44345094Smckusick SVCXPRT *xprt; 44445094Smckusick struct rpc_msg *msg; 44545094Smckusick char **replyp; 44645094Smckusick u_long *replylenp; 44745094Smckusick { 44845094Smckusick u_int loc; 44945094Smckusick register cache_ptr ent; 45045094Smckusick register struct svcudp_data *su = su_data(xprt); 45145094Smckusick register struct udp_cache *uc = (struct udp_cache *) su->su_cache; 45245094Smckusick 45345094Smckusick # define EQADDR(a1, a2) (bcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) 45445094Smckusick 45545094Smckusick loc = CACHE_LOC(xprt, su->su_xid); 45645094Smckusick for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 45745094Smckusick if (ent->cache_xid == su->su_xid && 45845094Smckusick ent->cache_proc == uc->uc_proc && 45945094Smckusick ent->cache_vers == uc->uc_vers && 46045094Smckusick ent->cache_prog == uc->uc_prog && 46145094Smckusick EQADDR(ent->cache_addr, uc->uc_addr)) { 46245094Smckusick *replyp = ent->cache_reply; 46345094Smckusick *replylenp = ent->cache_replylen; 46445094Smckusick return(1); 46545094Smckusick } 46645094Smckusick } 46745094Smckusick /* 46845094Smckusick * Failed to find entry 46945094Smckusick * Remember a few things so we can do a set later 47045094Smckusick */ 47145094Smckusick uc->uc_proc = msg->rm_call.cb_proc; 47245094Smckusick uc->uc_vers = msg->rm_call.cb_vers; 47345094Smckusick uc->uc_prog = msg->rm_call.cb_prog; 47445094Smckusick uc->uc_addr = xprt->xp_raddr; 47545094Smckusick return(0); 47645094Smckusick } 47745094Smckusick 478