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 static int cache_get(); 178 179 again: 180 xprt->xp_addrlen = sizeof(struct sockaddr_in); 181 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 182 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 183 if (rlen == -1 && errno == EINTR) 184 goto again; 185 if (rlen < (int)(4*sizeof(u_long))) 186 return (FALSE); 187 xdrs->x_op = XDR_DECODE; 188 XDR_SETPOS(xdrs, 0); 189 if (! xdr_callmsg(xdrs, msg)) 190 return (FALSE); 191 su->su_xid = msg->rm_xid; 192 if (su->su_cache != NULL) { 193 if (cache_get(xprt, msg, &reply, &replylen)) { 194 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 195 (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); 196 return (TRUE); 197 } 198 } 199 return (TRUE); 200 } 201 202 static bool_t 203 svcudp_reply(xprt, msg) 204 register SVCXPRT *xprt; 205 struct rpc_msg *msg; 206 { 207 register struct svcudp_data *su = su_data(xprt); 208 register XDR *xdrs = &(su->su_xdrs); 209 register int slen; 210 register bool_t stat = FALSE; 211 static void cache_set(); 212 213 xdrs->x_op = XDR_ENCODE; 214 XDR_SETPOS(xdrs, 0); 215 msg->rm_xid = su->su_xid; 216 if (xdr_replymsg(xdrs, msg)) { 217 slen = (int)XDR_GETPOS(xdrs); 218 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 219 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 220 == slen) { 221 stat = TRUE; 222 if (su->su_cache && slen >= 0) { 223 cache_set(xprt, (u_long) slen); 224 } 225 } 226 } 227 return (stat); 228 } 229 230 static bool_t 231 svcudp_getargs(xprt, xdr_args, args_ptr) 232 SVCXPRT *xprt; 233 xdrproc_t xdr_args; 234 caddr_t args_ptr; 235 { 236 237 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 238 } 239 240 static bool_t 241 svcudp_freeargs(xprt, xdr_args, args_ptr) 242 SVCXPRT *xprt; 243 xdrproc_t xdr_args; 244 caddr_t args_ptr; 245 { 246 register XDR *xdrs = &(su_data(xprt)->su_xdrs); 247 248 xdrs->x_op = XDR_FREE; 249 return ((*xdr_args)(xdrs, args_ptr)); 250 } 251 252 static void 253 svcudp_destroy(xprt) 254 register SVCXPRT *xprt; 255 { 256 register struct svcudp_data *su = su_data(xprt); 257 258 xprt_unregister(xprt); 259 (void)close(xprt->xp_sock); 260 XDR_DESTROY(&(su->su_xdrs)); 261 mem_free(rpc_buffer(xprt), su->su_iosz); 262 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 263 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 264 } 265 266 267 /***********this could be a separate file*********************/ 268 269 /* 270 * Fifo cache for udp server 271 * Copies pointers to reply buffers into fifo cache 272 * Buffers are sent again if retransmissions are detected. 273 */ 274 275 #define SPARSENESS 4 /* 75% sparse */ 276 277 #define CACHE_PERROR(msg) \ 278 (void) fprintf(stderr,"%s\n", msg) 279 280 #define ALLOC(type, size) \ 281 (type *) mem_alloc((unsigned) (sizeof(type) * (size))) 282 283 #define BZERO(addr, type, size) \ 284 bzero((char *) addr, sizeof(type) * (int) (size)) 285 286 /* 287 * An entry in the cache 288 */ 289 typedef struct cache_node *cache_ptr; 290 struct cache_node { 291 /* 292 * Index into cache is xid, proc, vers, prog and address 293 */ 294 u_long cache_xid; 295 u_long cache_proc; 296 u_long cache_vers; 297 u_long cache_prog; 298 struct sockaddr_in cache_addr; 299 /* 300 * The cached reply and length 301 */ 302 char * cache_reply; 303 u_long cache_replylen; 304 /* 305 * Next node on the list, if there is a collision 306 */ 307 cache_ptr cache_next; 308 }; 309 310 311 312 /* 313 * The entire cache 314 */ 315 struct udp_cache { 316 u_long uc_size; /* size of cache */ 317 cache_ptr *uc_entries; /* hash table of entries in cache */ 318 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 319 u_long uc_nextvictim; /* points to next victim in fifo list */ 320 u_long uc_prog; /* saved program number */ 321 u_long uc_vers; /* saved version number */ 322 u_long uc_proc; /* saved procedure number */ 323 struct sockaddr_in uc_addr; /* saved caller's address */ 324 }; 325 326 327 /* 328 * the hashing function 329 */ 330 #define CACHE_LOC(transp, xid) \ 331 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 332 333 334 /* 335 * Enable use of the cache. 336 * Note: there is no disable. 337 */ 338 svcudp_enablecache(transp, size) 339 SVCXPRT *transp; 340 u_long size; 341 { 342 struct svcudp_data *su = su_data(transp); 343 struct udp_cache *uc; 344 345 if (su->su_cache != NULL) { 346 CACHE_PERROR("enablecache: cache already enabled"); 347 return(0); 348 } 349 uc = ALLOC(struct udp_cache, 1); 350 if (uc == NULL) { 351 CACHE_PERROR("enablecache: could not allocate cache"); 352 return(0); 353 } 354 uc->uc_size = size; 355 uc->uc_nextvictim = 0; 356 uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 357 if (uc->uc_entries == NULL) { 358 CACHE_PERROR("enablecache: could not allocate cache data"); 359 return(0); 360 } 361 BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); 362 uc->uc_fifo = ALLOC(cache_ptr, size); 363 if (uc->uc_fifo == NULL) { 364 CACHE_PERROR("enablecache: could not allocate cache fifo"); 365 return(0); 366 } 367 BZERO(uc->uc_fifo, cache_ptr, size); 368 su->su_cache = (char *) uc; 369 return(1); 370 } 371 372 373 /* 374 * Set an entry in the cache 375 */ 376 static void 377 cache_set(xprt, replylen) 378 SVCXPRT *xprt; 379 u_long replylen; 380 { 381 register cache_ptr victim; 382 register cache_ptr *vicp; 383 register struct svcudp_data *su = su_data(xprt); 384 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 385 u_int loc; 386 char *newbuf; 387 388 /* 389 * Find space for the new entry, either by 390 * reusing an old entry, or by mallocing a new one 391 */ 392 victim = uc->uc_fifo[uc->uc_nextvictim]; 393 if (victim != NULL) { 394 loc = CACHE_LOC(xprt, victim->cache_xid); 395 for (vicp = &uc->uc_entries[loc]; 396 *vicp != NULL && *vicp != victim; 397 vicp = &(*vicp)->cache_next) 398 ; 399 if (*vicp == NULL) { 400 CACHE_PERROR("cache_set: victim not found"); 401 return; 402 } 403 *vicp = victim->cache_next; /* remote from cache */ 404 newbuf = victim->cache_reply; 405 } else { 406 victim = ALLOC(struct cache_node, 1); 407 if (victim == NULL) { 408 CACHE_PERROR("cache_set: victim alloc failed"); 409 return; 410 } 411 newbuf = mem_alloc(su->su_iosz); 412 if (newbuf == NULL) { 413 CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); 414 return; 415 } 416 } 417 418 /* 419 * Store it away 420 */ 421 victim->cache_replylen = replylen; 422 victim->cache_reply = rpc_buffer(xprt); 423 rpc_buffer(xprt) = newbuf; 424 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 425 victim->cache_xid = su->su_xid; 426 victim->cache_proc = uc->uc_proc; 427 victim->cache_vers = uc->uc_vers; 428 victim->cache_prog = uc->uc_prog; 429 victim->cache_addr = uc->uc_addr; 430 loc = CACHE_LOC(xprt, victim->cache_xid); 431 victim->cache_next = uc->uc_entries[loc]; 432 uc->uc_entries[loc] = victim; 433 uc->uc_fifo[uc->uc_nextvictim++] = victim; 434 uc->uc_nextvictim %= uc->uc_size; 435 } 436 437 /* 438 * Try to get an entry from the cache 439 * return 1 if found, 0 if not found 440 */ 441 static 442 cache_get(xprt, msg, replyp, replylenp) 443 SVCXPRT *xprt; 444 struct rpc_msg *msg; 445 char **replyp; 446 u_long *replylenp; 447 { 448 u_int loc; 449 register cache_ptr ent; 450 register struct svcudp_data *su = su_data(xprt); 451 register struct udp_cache *uc = (struct udp_cache *) su->su_cache; 452 453 # define EQADDR(a1, a2) (bcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) 454 455 loc = CACHE_LOC(xprt, su->su_xid); 456 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 457 if (ent->cache_xid == su->su_xid && 458 ent->cache_proc == uc->uc_proc && 459 ent->cache_vers == uc->uc_vers && 460 ent->cache_prog == uc->uc_prog && 461 EQADDR(ent->cache_addr, uc->uc_addr)) { 462 *replyp = ent->cache_reply; 463 *replylenp = ent->cache_replylen; 464 return(1); 465 } 466 } 467 /* 468 * Failed to find entry 469 * Remember a few things so we can do a set later 470 */ 471 uc->uc_proc = msg->rm_call.cb_proc; 472 uc->uc_vers = msg->rm_call.cb_vers; 473 uc->uc_prog = msg->rm_call.cb_prog; 474 uc->uc_addr = xprt->xp_raddr; 475 return(0); 476 } 477 478