1 /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ 2 /* 3 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 4 * unrestricted use provided that this legend is included on all tape 5 * media and as a part of the software program in whole or part. Users 6 * may copy or modify Sun RPC without charge, but are not authorized 7 * to license or distribute it to anyone else except as part of a product or 8 * program developed by the user. 9 * 10 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 11 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 12 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 13 * 14 * Sun RPC is provided with no support and without any obligation on the 15 * part of Sun Microsystems, Inc. to assist in its use, correction, 16 * modification or enhancement. 17 * 18 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 19 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 20 * OR ANY PART THEREOF. 21 * 22 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 23 * or profits or other special, indirect and consequential damages, even if 24 * Sun has been advised of the possibility of such damages. 25 * 26 * Sun Microsystems, Inc. 27 * 2550 Garcia Avenue 28 * Mountain View, California 94043 29 */ 30 #if !defined(lint) && defined(SCCSIDS) 31 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; 32 #endif 33 34 /* 35 * svc_udp.c, 36 * Server side for UDP/IP based RPC. (Does some caching in the hopes of 37 * achieving execute-at-most-once semantics.) 38 * 39 * Copyright (C) 1984, Sun Microsystems, Inc. 40 */ 41 42 #include <stdio.h> 43 #include <rpc/rpc.h> 44 #include <sys/socket.h> 45 #include <errno.h> 46 47 48 #define rpc_buffer(xprt) ((xprt)->xp_p1) 49 #define MAX(a, b) ((a > b) ? a : b) 50 51 static bool_t svcudp_recv(); 52 static bool_t svcudp_reply(); 53 static enum xprt_stat svcudp_stat(); 54 static bool_t svcudp_getargs(); 55 static bool_t svcudp_freeargs(); 56 static void svcudp_destroy(); 57 58 static struct xp_ops svcudp_op = { 59 svcudp_recv, 60 svcudp_stat, 61 svcudp_getargs, 62 svcudp_reply, 63 svcudp_freeargs, 64 svcudp_destroy 65 }; 66 67 extern int errno; 68 69 /* 70 * kept in xprt->xp_p2 71 */ 72 struct svcudp_data { 73 u_int su_iosz; /* byte size of send.recv buffer */ 74 u_long su_xid; /* transaction id */ 75 XDR su_xdrs; /* XDR handle */ 76 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ 77 char * su_cache; /* cached data, NULL if no cache */ 78 }; 79 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) 80 81 /* 82 * Usage: 83 * xprt = svcudp_create(sock); 84 * 85 * If sock<0 then a socket is created, else sock is used. 86 * If the socket, sock is not bound to a port then svcudp_create 87 * binds it to an arbitrary port. In any (successful) case, 88 * xprt->xp_sock is the registered socket number and xprt->xp_port is the 89 * associated port number. 90 * Once *xprt is initialized, it is registered as a transporter; 91 * see (svc.h, xprt_register). 92 * The routines returns NULL if a problem occurred. 93 */ 94 SVCXPRT * 95 svcudp_bufcreate(sock, sendsz, recvsz) 96 register int sock; 97 u_int sendsz, recvsz; 98 { 99 bool_t madesock = FALSE; 100 register SVCXPRT *xprt; 101 register struct svcudp_data *su; 102 struct sockaddr_in addr; 103 int len = sizeof(struct sockaddr_in); 104 105 if (sock == RPC_ANYSOCK) { 106 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 107 perror("svcudp_create: socket creation problem"); 108 return ((SVCXPRT *)NULL); 109 } 110 madesock = TRUE; 111 } 112 bzero((char *)&addr, sizeof (addr)); 113 addr.sin_family = AF_INET; 114 if (bindresvport(sock, &addr)) { 115 addr.sin_port = 0; 116 (void)bind(sock, (struct sockaddr *)&addr, len); 117 } 118 if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { 119 perror("svcudp_create - cannot getsockname"); 120 if (madesock) 121 (void)close(sock); 122 return ((SVCXPRT *)NULL); 123 } 124 xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); 125 if (xprt == NULL) { 126 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 127 return (NULL); 128 } 129 su = (struct svcudp_data *)mem_alloc(sizeof(*su)); 130 if (su == NULL) { 131 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 132 return (NULL); 133 } 134 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 135 if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) { 136 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 137 return (NULL); 138 } 139 xdrmem_create( 140 &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); 141 su->su_cache = NULL; 142 xprt->xp_p2 = (caddr_t)su; 143 xprt->xp_verf.oa_base = su->su_verfbody; 144 xprt->xp_ops = &svcudp_op; 145 xprt->xp_port = ntohs(addr.sin_port); 146 xprt->xp_sock = sock; 147 xprt_register(xprt); 148 return (xprt); 149 } 150 151 SVCXPRT * 152 svcudp_create(sock) 153 int sock; 154 { 155 156 return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 157 } 158 159 static enum xprt_stat 160 svcudp_stat(xprt) 161 SVCXPRT *xprt; 162 { 163 164 return (XPRT_IDLE); 165 } 166 167 static bool_t 168 svcudp_recv(xprt, msg) 169 register SVCXPRT *xprt; 170 struct rpc_msg *msg; 171 { 172 register struct svcudp_data *su = su_data(xprt); 173 register XDR *xdrs = &(su->su_xdrs); 174 register int rlen; 175 char *reply; 176 u_long replylen; 177 178 again: 179 xprt->xp_addrlen = sizeof(struct sockaddr_in); 180 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 181 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 182 if (rlen == -1 && errno == EINTR) 183 goto again; 184 if (rlen < 4*sizeof(u_long)) 185 return (FALSE); 186 xdrs->x_op = XDR_DECODE; 187 XDR_SETPOS(xdrs, 0); 188 if (! xdr_callmsg(xdrs, msg)) 189 return (FALSE); 190 su->su_xid = msg->rm_xid; 191 if (su->su_cache != NULL) { 192 if (cache_get(xprt, msg, &reply, &replylen)) { 193 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 194 (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); 195 return (TRUE); 196 } 197 } 198 return (TRUE); 199 } 200 201 static bool_t 202 svcudp_reply(xprt, msg) 203 register SVCXPRT *xprt; 204 struct rpc_msg *msg; 205 { 206 register struct svcudp_data *su = su_data(xprt); 207 register XDR *xdrs = &(su->su_xdrs); 208 register int slen; 209 register bool_t stat = FALSE; 210 211 xdrs->x_op = XDR_ENCODE; 212 XDR_SETPOS(xdrs, 0); 213 msg->rm_xid = su->su_xid; 214 if (xdr_replymsg(xdrs, msg)) { 215 slen = (int)XDR_GETPOS(xdrs); 216 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 217 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 218 == slen) { 219 stat = TRUE; 220 if (su->su_cache && slen >= 0) { 221 cache_set(xprt, (u_long) slen); 222 } 223 } 224 } 225 return (stat); 226 } 227 228 static bool_t 229 svcudp_getargs(xprt, xdr_args, args_ptr) 230 SVCXPRT *xprt; 231 xdrproc_t xdr_args; 232 caddr_t args_ptr; 233 { 234 235 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 236 } 237 238 static bool_t 239 svcudp_freeargs(xprt, xdr_args, args_ptr) 240 SVCXPRT *xprt; 241 xdrproc_t xdr_args; 242 caddr_t args_ptr; 243 { 244 register XDR *xdrs = &(su_data(xprt)->su_xdrs); 245 246 xdrs->x_op = XDR_FREE; 247 return ((*xdr_args)(xdrs, args_ptr)); 248 } 249 250 static void 251 svcudp_destroy(xprt) 252 register SVCXPRT *xprt; 253 { 254 register struct svcudp_data *su = su_data(xprt); 255 256 xprt_unregister(xprt); 257 (void)close(xprt->xp_sock); 258 XDR_DESTROY(&(su->su_xdrs)); 259 mem_free(rpc_buffer(xprt), su->su_iosz); 260 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 261 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 262 } 263 264 265 /***********this could be a separate file*********************/ 266 267 /* 268 * Fifo cache for udp server 269 * Copies pointers to reply buffers into fifo cache 270 * Buffers are sent again if retransmissions are detected. 271 */ 272 273 #define SPARSENESS 4 /* 75% sparse */ 274 275 #define CACHE_PERROR(msg) \ 276 (void) fprintf(stderr,"%s\n", msg) 277 278 #define ALLOC(type, size) \ 279 (type *) mem_alloc((unsigned) (sizeof(type) * (size))) 280 281 #define BZERO(addr, type, size) \ 282 bzero((char *) addr, sizeof(type) * (int) (size)) 283 284 /* 285 * An entry in the cache 286 */ 287 typedef struct cache_node *cache_ptr; 288 struct cache_node { 289 /* 290 * Index into cache is xid, proc, vers, prog and address 291 */ 292 u_long cache_xid; 293 u_long cache_proc; 294 u_long cache_vers; 295 u_long cache_prog; 296 struct sockaddr_in cache_addr; 297 /* 298 * The cached reply and length 299 */ 300 char * cache_reply; 301 u_long cache_replylen; 302 /* 303 * Next node on the list, if there is a collision 304 */ 305 cache_ptr cache_next; 306 }; 307 308 309 310 /* 311 * The entire cache 312 */ 313 struct udp_cache { 314 u_long uc_size; /* size of cache */ 315 cache_ptr *uc_entries; /* hash table of entries in cache */ 316 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 317 u_long uc_nextvictim; /* points to next victim in fifo list */ 318 u_long uc_prog; /* saved program number */ 319 u_long uc_vers; /* saved version number */ 320 u_long uc_proc; /* saved procedure number */ 321 struct sockaddr_in uc_addr; /* saved caller's address */ 322 }; 323 324 325 /* 326 * the hashing function 327 */ 328 #define CACHE_LOC(transp, xid) \ 329 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 330 331 332 /* 333 * Enable use of the cache. 334 * Note: there is no disable. 335 */ 336 svcudp_enablecache(transp, size) 337 SVCXPRT *transp; 338 u_long size; 339 { 340 struct svcudp_data *su = su_data(transp); 341 struct udp_cache *uc; 342 343 if (su->su_cache != NULL) { 344 CACHE_PERROR("enablecache: cache already enabled"); 345 return(0); 346 } 347 uc = ALLOC(struct udp_cache, 1); 348 if (uc == NULL) { 349 CACHE_PERROR("enablecache: could not allocate cache"); 350 return(0); 351 } 352 uc->uc_size = size; 353 uc->uc_nextvictim = 0; 354 uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 355 if (uc->uc_entries == NULL) { 356 CACHE_PERROR("enablecache: could not allocate cache data"); 357 return(0); 358 } 359 BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); 360 uc->uc_fifo = ALLOC(cache_ptr, size); 361 if (uc->uc_fifo == NULL) { 362 CACHE_PERROR("enablecache: could not allocate cache fifo"); 363 return(0); 364 } 365 BZERO(uc->uc_fifo, cache_ptr, size); 366 su->su_cache = (char *) uc; 367 return(1); 368 } 369 370 371 /* 372 * Set an entry in the cache 373 */ 374 static 375 cache_set(xprt, replylen) 376 SVCXPRT *xprt; 377 u_long replylen; 378 { 379 register cache_ptr victim; 380 register cache_ptr *vicp; 381 register struct svcudp_data *su = su_data(xprt); 382 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 383 u_int loc; 384 char *newbuf; 385 386 /* 387 * Find space for the new entry, either by 388 * reusing an old entry, or by mallocing a new one 389 */ 390 victim = uc->uc_fifo[uc->uc_nextvictim]; 391 if (victim != NULL) { 392 loc = CACHE_LOC(xprt, victim->cache_xid); 393 for (vicp = &uc->uc_entries[loc]; 394 *vicp != NULL && *vicp != victim; 395 vicp = &(*vicp)->cache_next) 396 ; 397 if (*vicp == NULL) { 398 CACHE_PERROR("cache_set: victim not found"); 399 return; 400 } 401 *vicp = victim->cache_next; /* remote from cache */ 402 newbuf = victim->cache_reply; 403 } else { 404 victim = ALLOC(struct cache_node, 1); 405 if (victim == NULL) { 406 CACHE_PERROR("cache_set: victim alloc failed"); 407 return; 408 } 409 newbuf = mem_alloc(su->su_iosz); 410 if (newbuf == NULL) { 411 CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); 412 return; 413 } 414 } 415 416 /* 417 * Store it away 418 */ 419 victim->cache_replylen = replylen; 420 victim->cache_reply = rpc_buffer(xprt); 421 rpc_buffer(xprt) = newbuf; 422 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 423 victim->cache_xid = su->su_xid; 424 victim->cache_proc = uc->uc_proc; 425 victim->cache_vers = uc->uc_vers; 426 victim->cache_prog = uc->uc_prog; 427 victim->cache_addr = uc->uc_addr; 428 loc = CACHE_LOC(xprt, victim->cache_xid); 429 victim->cache_next = uc->uc_entries[loc]; 430 uc->uc_entries[loc] = victim; 431 uc->uc_fifo[uc->uc_nextvictim++] = victim; 432 uc->uc_nextvictim %= uc->uc_size; 433 } 434 435 /* 436 * Try to get an entry from the cache 437 * return 1 if found, 0 if not found 438 */ 439 static 440 cache_get(xprt, msg, replyp, replylenp) 441 SVCXPRT *xprt; 442 struct rpc_msg *msg; 443 char **replyp; 444 u_long *replylenp; 445 { 446 u_int loc; 447 register cache_ptr ent; 448 register struct svcudp_data *su = su_data(xprt); 449 register struct udp_cache *uc = (struct udp_cache *) su->su_cache; 450 451 # define EQADDR(a1, a2) (bcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) 452 453 loc = CACHE_LOC(xprt, su->su_xid); 454 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 455 if (ent->cache_xid == su->su_xid && 456 ent->cache_proc == uc->uc_proc && 457 ent->cache_vers == uc->uc_vers && 458 ent->cache_prog == uc->uc_prog && 459 EQADDR(ent->cache_addr, uc->uc_addr)) { 460 *replyp = ent->cache_reply; 461 *replylenp = ent->cache_replylen; 462 return(1); 463 } 464 } 465 /* 466 * Failed to find entry 467 * Remember a few things so we can do a set later 468 */ 469 uc->uc_proc = msg->rm_call.cb_proc; 470 uc->uc_vers = msg->rm_call.cb_vers; 471 uc->uc_prog = msg->rm_call.cb_prog; 472 uc->uc_addr = xprt->xp_raddr; 473 return(0); 474 } 475 476