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