1 /* $OpenBSD: svc_udp.c,v 1.19 2010/09/01 14:43:34 millert Exp $ */ 2 3 /* 4 * Copyright (c) 2010, Oracle America, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * * Neither the name of the "Oracle America, Inc." nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 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 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <rpc/rpc.h> 44 #include <sys/socket.h> 45 #include <stdint.h> 46 #include <errno.h> 47 #include <unistd.h> 48 49 50 #define rpc_buffer(xprt) ((xprt)->xp_p1) 51 #define MAX(a, b) ((a > b) ? a : b) 52 53 static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *); 54 static enum xprt_stat svcudp_stat(SVCXPRT *); 55 static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, caddr_t); 56 static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *); 57 static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, caddr_t); 58 static void svcudp_destroy(SVCXPRT *); 59 static void cache_set(SVCXPRT *, u_long); 60 static int cache_get(SVCXPRT *, struct rpc_msg *, char **, 61 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(int sock, u_int sendsz, u_int recvsz) 99 { 100 bool_t madesock = FALSE; 101 SVCXPRT *xprt; 102 struct svcudp_data *su; 103 struct sockaddr_in addr; 104 socklen_t len = sizeof(struct sockaddr_in); 105 106 if (sock == RPC_ANYSOCK) { 107 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 108 perror("svcudp_create: socket creation problem"); 109 return (NULL); 110 } 111 madesock = TRUE; 112 } 113 memset(&addr, 0, sizeof (addr)); 114 addr.sin_len = sizeof(struct sockaddr_in); 115 addr.sin_family = AF_INET; 116 if (bindresvport(sock, &addr)) { 117 addr.sin_port = 0; 118 (void)bind(sock, (struct sockaddr *)&addr, len); 119 } 120 if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { 121 perror("svcudp_create - cannot getsockname"); 122 if (madesock) 123 (void)close(sock); 124 return (NULL); 125 } 126 xprt = (SVCXPRT *)malloc(sizeof(SVCXPRT)); 127 if (xprt == NULL) { 128 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 129 if (madesock) 130 (void)close(sock); 131 return (NULL); 132 } 133 su = (struct svcudp_data *)malloc(sizeof(*su)); 134 if (su == NULL) { 135 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 136 if (madesock) 137 (void)close(sock); 138 free(xprt); 139 return (NULL); 140 } 141 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 142 if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) { 143 (void)fprintf(stderr, "svcudp_create: out of memory\n"); 144 if (madesock) 145 (void)close(sock); 146 free(xprt); 147 free(su); 148 return (NULL); 149 } 150 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, 151 XDR_DECODE); 152 su->su_cache = NULL; 153 xprt->xp_p2 = (caddr_t)su; 154 xprt->xp_verf.oa_base = su->su_verfbody; 155 xprt->xp_ops = &svcudp_op; 156 xprt->xp_port = ntohs(addr.sin_port); 157 xprt->xp_sock = sock; 158 if (__xprt_register(xprt) == 0) { 159 if (madesock) 160 (void)close(sock); 161 free(rpc_buffer(xprt)); 162 free(xprt); 163 free(su); 164 return (NULL); 165 } 166 return (xprt); 167 } 168 169 SVCXPRT * 170 svcudp_create(int sock) 171 { 172 173 return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 174 } 175 176 /* ARGSUSED */ 177 static enum xprt_stat 178 svcudp_stat(SVCXPRT *xprt) 179 { 180 181 return (XPRT_IDLE); 182 } 183 184 static bool_t 185 svcudp_recv(SVCXPRT *xprt, struct rpc_msg *msg) 186 { 187 struct svcudp_data *su = su_data(xprt); 188 XDR *xdrs = &(su->su_xdrs); 189 int rlen; 190 char *reply; 191 u_long replylen; 192 193 again: 194 xprt->xp_addrlen = sizeof(struct sockaddr_in); 195 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 196 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 197 if (rlen == -1 && errno == EINTR) 198 goto again; 199 if (rlen == -1 || rlen < 4*sizeof(u_int32_t)) 200 return (FALSE); 201 xdrs->x_op = XDR_DECODE; 202 XDR_SETPOS(xdrs, 0); 203 if (! xdr_callmsg(xdrs, msg)) 204 return (FALSE); 205 su->su_xid = msg->rm_xid; 206 if (su->su_cache != NULL) { 207 if (cache_get(xprt, msg, &reply, &replylen)) { 208 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 209 (struct sockaddr *) &xprt->xp_raddr, 210 xprt->xp_addrlen); 211 return (TRUE); 212 } 213 } 214 return (TRUE); 215 } 216 217 static bool_t 218 svcudp_reply(SVCXPRT *xprt, struct rpc_msg *msg) 219 { 220 struct svcudp_data *su = su_data(xprt); 221 XDR *xdrs = &(su->su_xdrs); 222 int slen; 223 bool_t stat = FALSE; 224 225 xdrs->x_op = XDR_ENCODE; 226 XDR_SETPOS(xdrs, 0); 227 msg->rm_xid = su->su_xid; 228 if (xdr_replymsg(xdrs, msg)) { 229 slen = (int)XDR_GETPOS(xdrs); 230 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 231 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 232 == slen) { 233 stat = TRUE; 234 if (su->su_cache && slen >= 0) { 235 cache_set(xprt, (u_long) slen); 236 } 237 } 238 } 239 return (stat); 240 } 241 242 static bool_t 243 svcudp_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 244 { 245 246 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 247 } 248 249 static bool_t 250 svcudp_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 251 { 252 XDR *xdrs = &(su_data(xprt)->su_xdrs); 253 254 xdrs->x_op = XDR_FREE; 255 return ((*xdr_args)(xdrs, args_ptr)); 256 } 257 258 static void 259 svcudp_destroy(SVCXPRT *xprt) 260 { 261 struct svcudp_data *su = su_data(xprt); 262 263 xprt_unregister(xprt); 264 if (xprt->xp_sock != -1) 265 (void)close(xprt->xp_sock); 266 xprt->xp_sock = -1; 267 XDR_DESTROY(&(su->su_xdrs)); 268 mem_free(rpc_buffer(xprt), su->su_iosz); 269 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 270 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 271 } 272 273 /* 274 * Fifo cache for udp server 275 * Copies pointers to reply buffers into fifo cache 276 * Buffers are sent again if retransmissions are detected. 277 */ 278 279 #define SPARSENESS 4 /* 75% sparse */ 280 281 #define CACHE_PERROR(msg) \ 282 (void) fprintf(stderr,"%s\n", msg) 283 284 /* 285 * An entry in the cache 286 */ 287 typedef struct cache_node *cache_ptr; 288 struct cache_node { 289 /* 290 * Index into cache is xid, proc, vers, prog and address 291 */ 292 u_long cache_xid; 293 u_long cache_proc; 294 u_long cache_vers; 295 u_long cache_prog; 296 struct sockaddr_in cache_addr; 297 /* 298 * The cached reply and length 299 */ 300 char * cache_reply; 301 u_long cache_replylen; 302 /* 303 * Next node on the list, if there is a collision 304 */ 305 cache_ptr cache_next; 306 }; 307 308 /* 309 * The entire cache 310 */ 311 struct udp_cache { 312 u_long uc_size; /* size of cache */ 313 cache_ptr *uc_entries; /* hash table of entries in cache */ 314 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 315 u_long uc_nextvictim; /* points to next victim in fifo list */ 316 u_long uc_prog; /* saved program number */ 317 u_long uc_vers; /* saved version number */ 318 u_long uc_proc; /* saved procedure number */ 319 struct sockaddr_in uc_addr; /* saved caller's address */ 320 }; 321 322 323 /* 324 * the hashing function 325 */ 326 #define CACHE_LOC(transp, xid) \ 327 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 328 329 330 /* 331 * Enable use of the cache. 332 * Note: there is no disable. 333 */ 334 int 335 svcudp_enablecache(SVCXPRT *transp, u_long size) 336 { 337 struct svcudp_data *su = su_data(transp); 338 struct udp_cache *uc; 339 340 if (su->su_cache != NULL) { 341 CACHE_PERROR("enablecache: cache already enabled"); 342 return(0); 343 } 344 uc = malloc(sizeof(*uc)); 345 if (uc == NULL) { 346 CACHE_PERROR("enablecache: could not allocate cache"); 347 return(0); 348 } 349 uc->uc_size = size; 350 uc->uc_nextvictim = 0; 351 if (size > SIZE_MAX / (sizeof(cache_ptr) * SPARSENESS) || 352 (uc->uc_entries = calloc(sizeof(cache_ptr) * SPARSENESS, size)) == NULL) { 353 CACHE_PERROR("enablecache: could not allocate cache data"); 354 free(uc); 355 return(0); 356 } 357 uc->uc_fifo = calloc(sizeof(cache_ptr), size); 358 if (uc->uc_fifo == NULL) { 359 CACHE_PERROR("enablecache: could not allocate cache fifo"); 360 free(uc->uc_entries); 361 free(uc); 362 return(0); 363 } 364 su->su_cache = (char *) uc; 365 return(1); 366 } 367 368 369 /* 370 * Set an entry in the cache 371 */ 372 static void 373 cache_set(SVCXPRT *xprt, u_long replylen) 374 { 375 cache_ptr victim; 376 cache_ptr *vicp; 377 struct svcudp_data *su = su_data(xprt); 378 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 379 u_int loc; 380 char *newbuf; 381 382 /* 383 * Find space for the new entry, either by 384 * reusing an old entry, or by mallocing a new one 385 */ 386 victim = uc->uc_fifo[uc->uc_nextvictim]; 387 if (victim != NULL) { 388 loc = CACHE_LOC(xprt, victim->cache_xid); 389 for (vicp = &uc->uc_entries[loc]; 390 *vicp != NULL && *vicp != victim; 391 vicp = &(*vicp)->cache_next) 392 ; 393 if (*vicp == NULL) { 394 CACHE_PERROR("cache_set: victim not found"); 395 return; 396 } 397 *vicp = victim->cache_next; /* remote from cache */ 398 newbuf = victim->cache_reply; 399 } else { 400 victim = malloc(sizeof(struct cache_node)); 401 if (victim == NULL) { 402 CACHE_PERROR("cache_set: victim alloc failed"); 403 return; 404 } 405 newbuf = malloc(su->su_iosz); 406 if (newbuf == NULL) { 407 CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); 408 free(victim); 409 return; 410 } 411 } 412 413 /* 414 * Store it away 415 */ 416 victim->cache_replylen = replylen; 417 victim->cache_reply = rpc_buffer(xprt); 418 rpc_buffer(xprt) = newbuf; 419 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 420 victim->cache_xid = su->su_xid; 421 victim->cache_proc = uc->uc_proc; 422 victim->cache_vers = uc->uc_vers; 423 victim->cache_prog = uc->uc_prog; 424 victim->cache_addr = uc->uc_addr; 425 loc = CACHE_LOC(xprt, victim->cache_xid); 426 victim->cache_next = uc->uc_entries[loc]; 427 uc->uc_entries[loc] = victim; 428 uc->uc_fifo[uc->uc_nextvictim++] = victim; 429 uc->uc_nextvictim %= uc->uc_size; 430 } 431 432 /* 433 * Try to get an entry from the cache 434 * return 1 if found, 0 if not found 435 */ 436 static int 437 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp) 438 { 439 u_int loc; 440 cache_ptr ent; 441 struct svcudp_data *su = su_data(xprt); 442 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 443 444 # define EQADDR(a1, a2) (memcmp(&a1, &a2, sizeof(a1)) == 0) 445 446 loc = CACHE_LOC(xprt, su->su_xid); 447 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 448 if (ent->cache_xid == su->su_xid && 449 ent->cache_proc == uc->uc_proc && 450 ent->cache_vers == uc->uc_vers && 451 ent->cache_prog == uc->uc_prog && 452 EQADDR(ent->cache_addr, uc->uc_addr)) { 453 *replyp = ent->cache_reply; 454 *replylenp = ent->cache_replylen; 455 return(1); 456 } 457 } 458 /* 459 * Failed to find entry 460 * Remember a few things so we can do a set later 461 */ 462 uc->uc_proc = msg->rm_call.cb_proc; 463 uc->uc_vers = msg->rm_call.cb_vers; 464 uc->uc_prog = msg->rm_call.cb_prog; 465 uc->uc_addr = xprt->xp_raddr; 466 return(0); 467 } 468 469