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