1 /* $OpenBSD: svc_udp.c,v 1.18 2009/06/07 01:21:00 millert 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 <stdint.h> 45 #include <errno.h> 46 #include <unistd.h> 47 48 49 #define rpc_buffer(xprt) ((xprt)->xp_p1) 50 #define MAX(a, b) ((a > b) ? a : b) 51 52 static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *); 53 static enum xprt_stat svcudp_stat(SVCXPRT *); 54 static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, caddr_t); 55 static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *); 56 static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, caddr_t); 57 static void svcudp_destroy(SVCXPRT *); 58 static void cache_set(SVCXPRT *, u_long); 59 static int cache_get(SVCXPRT *, struct rpc_msg *, char **, 60 u_long *); 61 62 static struct xp_ops svcudp_op = { 63 svcudp_recv, 64 svcudp_stat, 65 svcudp_getargs, 66 svcudp_reply, 67 svcudp_freeargs, 68 svcudp_destroy 69 }; 70 71 /* 72 * kept in xprt->xp_p2 73 */ 74 struct svcudp_data { 75 u_int su_iosz; /* byte size of send.recv buffer */ 76 u_long su_xid; /* transaction id */ 77 XDR su_xdrs; /* XDR handle */ 78 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ 79 char * su_cache; /* cached data, NULL if no cache */ 80 }; 81 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) 82 83 /* 84 * Usage: 85 * xprt = svcudp_create(sock); 86 * 87 * If sock<0 then a socket is created, else sock is used. 88 * If the socket, sock is not bound to a port then svcudp_create 89 * binds it to an arbitrary port. In any (successful) case, 90 * xprt->xp_sock is the registered socket number and xprt->xp_port is the 91 * associated port number. 92 * Once *xprt is initialized, it is registered as a transporter; 93 * see (svc.h, xprt_register). 94 * The routines returns NULL if a problem occurred. 95 */ 96 SVCXPRT * 97 svcudp_bufcreate(int sock, u_int sendsz, u_int recvsz) 98 { 99 bool_t madesock = FALSE; 100 SVCXPRT *xprt; 101 struct svcudp_data *su; 102 struct sockaddr_in addr; 103 socklen_t 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 (NULL); 109 } 110 madesock = TRUE; 111 } 112 memset(&addr, 0, sizeof (addr)); 113 addr.sin_len = sizeof(struct sockaddr_in); 114 addr.sin_family = AF_INET; 115 if (bindresvport(sock, &addr)) { 116 addr.sin_port = 0; 117 (void)bind(sock, (struct sockaddr *)&addr, len); 118 } 119 if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { 120 perror("svcudp_create - cannot getsockname"); 121 if (madesock) 122 (void)close(sock); 123 return (NULL); 124 } 125 xprt = (SVCXPRT *)malloc(sizeof(SVCXPRT)); 126 if (xprt == NULL) { 127 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 128 if (madesock) 129 (void)close(sock); 130 return (NULL); 131 } 132 su = (struct svcudp_data *)malloc(sizeof(*su)); 133 if (su == NULL) { 134 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 135 if (madesock) 136 (void)close(sock); 137 free(xprt); 138 return (NULL); 139 } 140 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 141 if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) { 142 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 143 if (madesock) 144 (void)close(sock); 145 free(xprt); 146 free(su); 147 return (NULL); 148 } 149 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, 150 XDR_DECODE); 151 su->su_cache = NULL; 152 xprt->xp_p2 = (caddr_t)su; 153 xprt->xp_verf.oa_base = su->su_verfbody; 154 xprt->xp_ops = &svcudp_op; 155 xprt->xp_port = ntohs(addr.sin_port); 156 xprt->xp_sock = sock; 157 if (__xprt_register(xprt) == 0) { 158 if (madesock) 159 (void)close(sock); 160 free(rpc_buffer(xprt)); 161 free(xprt); 162 free(su); 163 return (NULL); 164 } 165 return (xprt); 166 } 167 168 SVCXPRT * 169 svcudp_create(int sock) 170 { 171 172 return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 173 } 174 175 /* ARGSUSED */ 176 static enum xprt_stat 177 svcudp_stat(SVCXPRT *xprt) 178 { 179 180 return (XPRT_IDLE); 181 } 182 183 static bool_t 184 svcudp_recv(SVCXPRT *xprt, struct rpc_msg *msg) 185 { 186 struct svcudp_data *su = su_data(xprt); 187 XDR *xdrs = &(su->su_xdrs); 188 int rlen; 189 char *reply; 190 u_long replylen; 191 192 again: 193 xprt->xp_addrlen = sizeof(struct sockaddr_in); 194 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 195 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 196 if (rlen == -1 && errno == EINTR) 197 goto again; 198 if (rlen == -1 || rlen < 4*sizeof(u_int32_t)) 199 return (FALSE); 200 xdrs->x_op = XDR_DECODE; 201 XDR_SETPOS(xdrs, 0); 202 if (! xdr_callmsg(xdrs, msg)) 203 return (FALSE); 204 su->su_xid = msg->rm_xid; 205 if (su->su_cache != NULL) { 206 if (cache_get(xprt, msg, &reply, &replylen)) { 207 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 208 (struct sockaddr *) &xprt->xp_raddr, 209 xprt->xp_addrlen); 210 return (TRUE); 211 } 212 } 213 return (TRUE); 214 } 215 216 static bool_t 217 svcudp_reply(SVCXPRT *xprt, struct rpc_msg *msg) 218 { 219 struct svcudp_data *su = su_data(xprt); 220 XDR *xdrs = &(su->su_xdrs); 221 int slen; 222 bool_t stat = FALSE; 223 224 xdrs->x_op = XDR_ENCODE; 225 XDR_SETPOS(xdrs, 0); 226 msg->rm_xid = su->su_xid; 227 if (xdr_replymsg(xdrs, msg)) { 228 slen = (int)XDR_GETPOS(xdrs); 229 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 230 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 231 == slen) { 232 stat = TRUE; 233 if (su->su_cache && slen >= 0) { 234 cache_set(xprt, (u_long) slen); 235 } 236 } 237 } 238 return (stat); 239 } 240 241 static bool_t 242 svcudp_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 243 { 244 245 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 246 } 247 248 static bool_t 249 svcudp_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 250 { 251 XDR *xdrs = &(su_data(xprt)->su_xdrs); 252 253 xdrs->x_op = XDR_FREE; 254 return ((*xdr_args)(xdrs, args_ptr)); 255 } 256 257 static void 258 svcudp_destroy(SVCXPRT *xprt) 259 { 260 struct svcudp_data *su = su_data(xprt); 261 262 xprt_unregister(xprt); 263 if (xprt->xp_sock != -1) 264 (void)close(xprt->xp_sock); 265 xprt->xp_sock = -1; 266 XDR_DESTROY(&(su->su_xdrs)); 267 mem_free(rpc_buffer(xprt), su->su_iosz); 268 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 269 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 270 } 271 272 /* 273 * Fifo cache for udp server 274 * Copies pointers to reply buffers into fifo cache 275 * Buffers are sent again if retransmissions are detected. 276 */ 277 278 #define SPARSENESS 4 /* 75% sparse */ 279 280 #define CACHE_PERROR(msg) \ 281 (void) fprintf(stderr,"%s\n", msg) 282 283 /* 284 * An entry in the cache 285 */ 286 typedef struct cache_node *cache_ptr; 287 struct cache_node { 288 /* 289 * Index into cache is xid, proc, vers, prog and address 290 */ 291 u_long cache_xid; 292 u_long cache_proc; 293 u_long cache_vers; 294 u_long cache_prog; 295 struct sockaddr_in cache_addr; 296 /* 297 * The cached reply and length 298 */ 299 char * cache_reply; 300 u_long cache_replylen; 301 /* 302 * Next node on the list, if there is a collision 303 */ 304 cache_ptr cache_next; 305 }; 306 307 /* 308 * The entire cache 309 */ 310 struct udp_cache { 311 u_long uc_size; /* size of cache */ 312 cache_ptr *uc_entries; /* hash table of entries in cache */ 313 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 314 u_long uc_nextvictim; /* points to next victim in fifo list */ 315 u_long uc_prog; /* saved program number */ 316 u_long uc_vers; /* saved version number */ 317 u_long uc_proc; /* saved procedure number */ 318 struct sockaddr_in uc_addr; /* saved caller's address */ 319 }; 320 321 322 /* 323 * the hashing function 324 */ 325 #define CACHE_LOC(transp, xid) \ 326 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 327 328 329 /* 330 * Enable use of the cache. 331 * Note: there is no disable. 332 */ 333 int 334 svcudp_enablecache(SVCXPRT *transp, u_long size) 335 { 336 struct svcudp_data *su = su_data(transp); 337 struct udp_cache *uc; 338 339 if (su->su_cache != NULL) { 340 CACHE_PERROR("enablecache: cache already enabled"); 341 return(0); 342 } 343 uc = malloc(sizeof(*uc)); 344 if (uc == NULL) { 345 CACHE_PERROR("enablecache: could not allocate cache"); 346 return(0); 347 } 348 uc->uc_size = size; 349 uc->uc_nextvictim = 0; 350 if (size > SIZE_MAX / (sizeof(cache_ptr) * SPARSENESS) || 351 (uc->uc_entries = calloc(sizeof(cache_ptr) * SPARSENESS, size)) == NULL) { 352 CACHE_PERROR("enablecache: could not allocate cache data"); 353 free(uc); 354 return(0); 355 } 356 uc->uc_fifo = calloc(sizeof(cache_ptr), size); 357 if (uc->uc_fifo == NULL) { 358 CACHE_PERROR("enablecache: could not allocate cache fifo"); 359 free(uc->uc_entries); 360 free(uc); 361 return(0); 362 } 363 su->su_cache = (char *) uc; 364 return(1); 365 } 366 367 368 /* 369 * Set an entry in the cache 370 */ 371 static void 372 cache_set(SVCXPRT *xprt, u_long replylen) 373 { 374 cache_ptr victim; 375 cache_ptr *vicp; 376 struct svcudp_data *su = su_data(xprt); 377 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 378 u_int loc; 379 char *newbuf; 380 381 /* 382 * Find space for the new entry, either by 383 * reusing an old entry, or by mallocing a new one 384 */ 385 victim = uc->uc_fifo[uc->uc_nextvictim]; 386 if (victim != NULL) { 387 loc = CACHE_LOC(xprt, victim->cache_xid); 388 for (vicp = &uc->uc_entries[loc]; 389 *vicp != NULL && *vicp != victim; 390 vicp = &(*vicp)->cache_next) 391 ; 392 if (*vicp == NULL) { 393 CACHE_PERROR("cache_set: victim not found"); 394 return; 395 } 396 *vicp = victim->cache_next; /* remote from cache */ 397 newbuf = victim->cache_reply; 398 } else { 399 victim = malloc(sizeof(struct cache_node)); 400 if (victim == NULL) { 401 CACHE_PERROR("cache_set: victim alloc failed"); 402 return; 403 } 404 newbuf = malloc(su->su_iosz); 405 if (newbuf == NULL) { 406 CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); 407 free(victim); 408 return; 409 } 410 } 411 412 /* 413 * Store it away 414 */ 415 victim->cache_replylen = replylen; 416 victim->cache_reply = rpc_buffer(xprt); 417 rpc_buffer(xprt) = newbuf; 418 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 419 victim->cache_xid = su->su_xid; 420 victim->cache_proc = uc->uc_proc; 421 victim->cache_vers = uc->uc_vers; 422 victim->cache_prog = uc->uc_prog; 423 victim->cache_addr = uc->uc_addr; 424 loc = CACHE_LOC(xprt, victim->cache_xid); 425 victim->cache_next = uc->uc_entries[loc]; 426 uc->uc_entries[loc] = victim; 427 uc->uc_fifo[uc->uc_nextvictim++] = victim; 428 uc->uc_nextvictim %= uc->uc_size; 429 } 430 431 /* 432 * Try to get an entry from the cache 433 * return 1 if found, 0 if not found 434 */ 435 static int 436 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp) 437 { 438 u_int loc; 439 cache_ptr ent; 440 struct svcudp_data *su = su_data(xprt); 441 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 442 443 # define EQADDR(a1, a2) (memcmp(&a1, &a2, sizeof(a1)) == 0) 444 445 loc = CACHE_LOC(xprt, su->su_xid); 446 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 447 if (ent->cache_xid == su->su_xid && 448 ent->cache_proc == uc->uc_proc && 449 ent->cache_vers == uc->uc_vers && 450 ent->cache_prog == uc->uc_prog && 451 EQADDR(ent->cache_addr, uc->uc_addr)) { 452 *replyp = ent->cache_reply; 453 *replylenp = ent->cache_replylen; 454 return(1); 455 } 456 } 457 /* 458 * Failed to find entry 459 * Remember a few things so we can do a set later 460 */ 461 uc->uc_proc = msg->rm_call.cb_proc; 462 uc->uc_vers = msg->rm_call.cb_vers; 463 uc->uc_prog = msg->rm_call.cb_prog; 464 uc->uc_addr = xprt->xp_raddr; 465 return(0); 466 } 467 468