1 /* $OpenBSD: svc_udp.c,v 1.16 2005/08/08 08:05:35 espie Exp $ */ 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 31 /* 32 * svc_udp.c, 33 * Server side for UDP/IP based RPC. (Does some caching in the hopes of 34 * achieving execute-at-most-once semantics.) 35 * 36 * Copyright (C) 1984, Sun Microsystems, Inc. 37 */ 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <rpc/rpc.h> 43 #include <sys/socket.h> 44 #include <errno.h> 45 #include <unistd.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(SVCXPRT *, struct rpc_msg *); 52 static enum xprt_stat svcudp_stat(SVCXPRT *); 53 static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, caddr_t); 54 static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *); 55 static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, caddr_t); 56 static void svcudp_destroy(SVCXPRT *); 57 static void cache_set(SVCXPRT *, u_long); 58 static int cache_get(SVCXPRT *, struct rpc_msg *, char **, 59 u_long *); 60 61 static struct xp_ops svcudp_op = { 62 svcudp_recv, 63 svcudp_stat, 64 svcudp_getargs, 65 svcudp_reply, 66 svcudp_freeargs, 67 svcudp_destroy 68 }; 69 70 /* 71 * kept in xprt->xp_p2 72 */ 73 struct svcudp_data { 74 u_int su_iosz; /* byte size of send.recv buffer */ 75 u_long su_xid; /* transaction id */ 76 XDR su_xdrs; /* XDR handle */ 77 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ 78 char * su_cache; /* cached data, NULL if no cache */ 79 }; 80 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) 81 82 /* 83 * Usage: 84 * xprt = svcudp_create(sock); 85 * 86 * If sock<0 then a socket is created, else sock is used. 87 * If the socket, sock is not bound to a port then svcudp_create 88 * binds it to an arbitrary port. In any (successful) case, 89 * xprt->xp_sock is the registered socket number and xprt->xp_port is the 90 * associated port number. 91 * Once *xprt is initialized, it is registered as a transporter; 92 * see (svc.h, xprt_register). 93 * The routines returns NULL if a problem occurred. 94 */ 95 SVCXPRT * 96 svcudp_bufcreate(int sock, u_int sendsz, u_int recvsz) 97 { 98 bool_t madesock = FALSE; 99 SVCXPRT *xprt; 100 struct svcudp_data *su; 101 struct sockaddr_in addr; 102 socklen_t len = sizeof(struct sockaddr_in); 103 104 if (sock == RPC_ANYSOCK) { 105 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 106 perror("svcudp_create: socket creation problem"); 107 return (NULL); 108 } 109 madesock = TRUE; 110 } 111 memset(&addr, 0, sizeof (addr)); 112 addr.sin_len = sizeof(struct sockaddr_in); 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 (NULL); 123 } 124 xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); 125 if (xprt == NULL) { 126 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 127 if (madesock) 128 (void)close(sock); 129 return (NULL); 130 } 131 su = (struct svcudp_data *)mem_alloc(sizeof(*su)); 132 if (su == NULL) { 133 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 134 if (madesock) 135 (void)close(sock); 136 free(xprt); 137 return (NULL); 138 } 139 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 140 if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) { 141 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 142 if (madesock) 143 (void)close(sock); 144 free(xprt); 145 free(su); 146 return (NULL); 147 } 148 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, 149 XDR_DECODE); 150 su->su_cache = NULL; 151 xprt->xp_p2 = (caddr_t)su; 152 xprt->xp_verf.oa_base = su->su_verfbody; 153 xprt->xp_ops = &svcudp_op; 154 xprt->xp_port = ntohs(addr.sin_port); 155 xprt->xp_sock = sock; 156 if (__xprt_register(xprt) == 0) { 157 if (madesock) 158 (void)close(sock); 159 free(rpc_buffer(xprt)); 160 free(xprt); 161 free(su); 162 return (NULL); 163 } 164 return (xprt); 165 } 166 167 SVCXPRT * 168 svcudp_create(int sock) 169 { 170 171 return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 172 } 173 174 /* ARGSUSED */ 175 static enum xprt_stat 176 svcudp_stat(SVCXPRT *xprt) 177 { 178 179 return (XPRT_IDLE); 180 } 181 182 static bool_t 183 svcudp_recv(SVCXPRT *xprt, struct rpc_msg *msg) 184 { 185 struct svcudp_data *su = su_data(xprt); 186 XDR *xdrs = &(su->su_xdrs); 187 int rlen; 188 char *reply; 189 u_long replylen; 190 191 again: 192 xprt->xp_addrlen = sizeof(struct sockaddr_in); 193 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 194 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 195 if (rlen == -1 && errno == EINTR) 196 goto again; 197 if (rlen == -1 || rlen < 4*sizeof(u_int32_t)) 198 return (FALSE); 199 xdrs->x_op = XDR_DECODE; 200 XDR_SETPOS(xdrs, 0); 201 if (! xdr_callmsg(xdrs, msg)) 202 return (FALSE); 203 su->su_xid = msg->rm_xid; 204 if (su->su_cache != NULL) { 205 if (cache_get(xprt, msg, &reply, &replylen)) { 206 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 207 (struct sockaddr *) &xprt->xp_raddr, 208 xprt->xp_addrlen); 209 return (TRUE); 210 } 211 } 212 return (TRUE); 213 } 214 215 static bool_t 216 svcudp_reply(SVCXPRT *xprt, struct rpc_msg *msg) 217 { 218 struct svcudp_data *su = su_data(xprt); 219 XDR *xdrs = &(su->su_xdrs); 220 int slen; 221 bool_t stat = FALSE; 222 223 xdrs->x_op = XDR_ENCODE; 224 XDR_SETPOS(xdrs, 0); 225 msg->rm_xid = su->su_xid; 226 if (xdr_replymsg(xdrs, msg)) { 227 slen = (int)XDR_GETPOS(xdrs); 228 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 229 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 230 == slen) { 231 stat = TRUE; 232 if (su->su_cache && slen >= 0) { 233 cache_set(xprt, (u_long) slen); 234 } 235 } 236 } 237 return (stat); 238 } 239 240 static bool_t 241 svcudp_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 242 { 243 244 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 245 } 246 247 static bool_t 248 svcudp_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 249 { 250 XDR *xdrs = &(su_data(xprt)->su_xdrs); 251 252 xdrs->x_op = XDR_FREE; 253 return ((*xdr_args)(xdrs, args_ptr)); 254 } 255 256 static void 257 svcudp_destroy(SVCXPRT *xprt) 258 { 259 struct svcudp_data *su = su_data(xprt); 260 261 xprt_unregister(xprt); 262 if (xprt->xp_sock != -1) 263 (void)close(xprt->xp_sock); 264 xprt->xp_sock = -1; 265 XDR_DESTROY(&(su->su_xdrs)); 266 mem_free(rpc_buffer(xprt), su->su_iosz); 267 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 268 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 269 } 270 271 /* 272 * Fifo cache for udp server 273 * Copies pointers to reply buffers into fifo cache 274 * Buffers are sent again if retransmissions are detected. 275 */ 276 277 #define SPARSENESS 4 /* 75% sparse */ 278 279 #define CACHE_PERROR(msg) \ 280 (void) fprintf(stderr,"%s\n", msg) 281 282 #define ALLOC(type, size) \ 283 (type *) mem_alloc((unsigned) (sizeof(type) * (size))) 284 285 #define BZERO(addr, type, size) \ 286 memset((char *) addr, 0, sizeof(type) * (int) (size)) 287 288 /* 289 * An entry in the cache 290 */ 291 typedef struct cache_node *cache_ptr; 292 struct cache_node { 293 /* 294 * Index into cache is xid, proc, vers, prog and address 295 */ 296 u_long cache_xid; 297 u_long cache_proc; 298 u_long cache_vers; 299 u_long cache_prog; 300 struct sockaddr_in cache_addr; 301 /* 302 * The cached reply and length 303 */ 304 char * cache_reply; 305 u_long cache_replylen; 306 /* 307 * Next node on the list, if there is a collision 308 */ 309 cache_ptr cache_next; 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 int 339 svcudp_enablecache(SVCXPRT *transp, u_long size) 340 { 341 struct svcudp_data *su = su_data(transp); 342 struct udp_cache *uc; 343 344 if (su->su_cache != NULL) { 345 CACHE_PERROR("enablecache: cache already enabled"); 346 return(0); 347 } 348 uc = ALLOC(struct udp_cache, 1); 349 if (uc == NULL) { 350 CACHE_PERROR("enablecache: could not allocate cache"); 351 return(0); 352 } 353 uc->uc_size = size; 354 uc->uc_nextvictim = 0; 355 uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 356 if (uc->uc_entries == NULL) { 357 CACHE_PERROR("enablecache: could not allocate cache data"); 358 free(uc); 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 free(uc->uc_entries); 366 free(uc); 367 return(0); 368 } 369 BZERO(uc->uc_fifo, cache_ptr, size); 370 su->su_cache = (char *) uc; 371 return(1); 372 } 373 374 375 /* 376 * Set an entry in the cache 377 */ 378 static void 379 cache_set(SVCXPRT *xprt, u_long replylen) 380 { 381 cache_ptr victim; 382 cache_ptr *vicp; 383 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 free(victim); 415 return; 416 } 417 } 418 419 /* 420 * Store it away 421 */ 422 victim->cache_replylen = replylen; 423 victim->cache_reply = rpc_buffer(xprt); 424 rpc_buffer(xprt) = newbuf; 425 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 426 victim->cache_xid = su->su_xid; 427 victim->cache_proc = uc->uc_proc; 428 victim->cache_vers = uc->uc_vers; 429 victim->cache_prog = uc->uc_prog; 430 victim->cache_addr = uc->uc_addr; 431 loc = CACHE_LOC(xprt, victim->cache_xid); 432 victim->cache_next = uc->uc_entries[loc]; 433 uc->uc_entries[loc] = victim; 434 uc->uc_fifo[uc->uc_nextvictim++] = victim; 435 uc->uc_nextvictim %= uc->uc_size; 436 } 437 438 /* 439 * Try to get an entry from the cache 440 * return 1 if found, 0 if not found 441 */ 442 static int 443 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp) 444 { 445 u_int loc; 446 cache_ptr ent; 447 struct svcudp_data *su = su_data(xprt); 448 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 449 450 # define EQADDR(a1, a2) (memcmp(&a1, &a2, sizeof(a1)) == 0) 451 452 loc = CACHE_LOC(xprt, su->su_xid); 453 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 454 if (ent->cache_xid == su->su_xid && 455 ent->cache_proc == uc->uc_proc && 456 ent->cache_vers == uc->uc_vers && 457 ent->cache_prog == uc->uc_prog && 458 EQADDR(ent->cache_addr, uc->uc_addr)) { 459 *replyp = ent->cache_reply; 460 *replylenp = ent->cache_replylen; 461 return(1); 462 } 463 } 464 /* 465 * Failed to find entry 466 * Remember a few things so we can do a set later 467 */ 468 uc->uc_proc = msg->rm_call.cb_proc; 469 uc->uc_vers = msg->rm_call.cb_vers; 470 uc->uc_prog = msg->rm_call.cb_prog; 471 uc->uc_addr = xprt->xp_raddr; 472 return(0); 473 } 474 475