1 /* $OpenBSD: svc_udp.c,v 1.26 2019/06/28 13:32:42 deraadt 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)) == -1) 108 return (NULL); 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 if (madesock) 120 (void)close(sock); 121 return (NULL); 122 } 123 xprt = malloc(sizeof(SVCXPRT)); 124 if (xprt == NULL) { 125 if (madesock) 126 (void)close(sock); 127 return (NULL); 128 } 129 su = malloc(sizeof(*su)); 130 if (su == NULL) { 131 if (madesock) 132 (void)close(sock); 133 free(xprt); 134 return (NULL); 135 } 136 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 137 if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) { 138 if (madesock) 139 (void)close(sock); 140 free(xprt); 141 free(su); 142 return (NULL); 143 } 144 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, 145 XDR_DECODE); 146 su->su_cache = NULL; 147 xprt->xp_p2 = (caddr_t)su; 148 xprt->xp_verf.oa_base = su->su_verfbody; 149 xprt->xp_ops = &svcudp_op; 150 xprt->xp_port = ntohs(addr.sin_port); 151 xprt->xp_sock = sock; 152 if (__xprt_register(xprt) == 0) { 153 if (madesock) 154 (void)close(sock); 155 free(rpc_buffer(xprt)); 156 free(xprt); 157 free(su); 158 return (NULL); 159 } 160 return (xprt); 161 } 162 DEF_WEAK(svcudp_bufcreate); 163 164 SVCXPRT * 165 svcudp_create(int sock) 166 { 167 168 return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 169 } 170 DEF_WEAK(svcudp_create); 171 172 static enum xprt_stat 173 svcudp_stat(SVCXPRT *xprt) 174 { 175 176 return (XPRT_IDLE); 177 } 178 179 static bool_t 180 svcudp_recv(SVCXPRT *xprt, struct rpc_msg *msg) 181 { 182 struct svcudp_data *su = su_data(xprt); 183 XDR *xdrs = &(su->su_xdrs); 184 int rlen; 185 char *reply; 186 u_long replylen; 187 188 again: 189 xprt->xp_addrlen = sizeof(struct sockaddr_in); 190 rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 191 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 192 if (rlen == -1 && errno == EINTR) 193 goto again; 194 if (rlen == -1 || rlen < 4*sizeof(u_int32_t)) 195 return (FALSE); 196 xdrs->x_op = XDR_DECODE; 197 XDR_SETPOS(xdrs, 0); 198 if (! xdr_callmsg(xdrs, msg)) 199 return (FALSE); 200 su->su_xid = msg->rm_xid; 201 if (su->su_cache != NULL) { 202 if (cache_get(xprt, msg, &reply, &replylen)) { 203 (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, 204 (struct sockaddr *) &xprt->xp_raddr, 205 xprt->xp_addrlen); 206 return (TRUE); 207 } 208 } 209 return (TRUE); 210 } 211 212 static bool_t 213 svcudp_reply(SVCXPRT *xprt, struct rpc_msg *msg) 214 { 215 struct svcudp_data *su = su_data(xprt); 216 XDR *xdrs = &(su->su_xdrs); 217 int slen; 218 bool_t stat = FALSE; 219 220 xdrs->x_op = XDR_ENCODE; 221 XDR_SETPOS(xdrs, 0); 222 msg->rm_xid = su->su_xid; 223 if (xdr_replymsg(xdrs, msg)) { 224 slen = (int)XDR_GETPOS(xdrs); 225 if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 226 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 227 == slen) { 228 stat = TRUE; 229 if (su->su_cache && slen >= 0) { 230 cache_set(xprt, (u_long) slen); 231 } 232 } 233 } 234 return (stat); 235 } 236 237 static bool_t 238 svcudp_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 239 { 240 241 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 242 } 243 244 static bool_t 245 svcudp_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 246 { 247 XDR *xdrs = &(su_data(xprt)->su_xdrs); 248 249 xdrs->x_op = XDR_FREE; 250 return ((*xdr_args)(xdrs, args_ptr)); 251 } 252 253 static void 254 svcudp_destroy(SVCXPRT *xprt) 255 { 256 struct svcudp_data *su = su_data(xprt); 257 258 xprt_unregister(xprt); 259 if (xprt->xp_sock != -1) 260 (void)close(xprt->xp_sock); 261 xprt->xp_sock = -1; 262 XDR_DESTROY(&(su->su_xdrs)); 263 mem_free(rpc_buffer(xprt), su->su_iosz); 264 mem_free((caddr_t)su, sizeof(struct svcudp_data)); 265 mem_free((caddr_t)xprt, sizeof(SVCXPRT)); 266 } 267 268 /* 269 * Fifo cache for udp server 270 * Copies pointers to reply buffers into fifo cache 271 * Buffers are sent again if retransmissions are detected. 272 */ 273 274 #define SPARSENESS 4 /* 75% sparse */ 275 276 /* 277 * An entry in the cache 278 */ 279 typedef struct cache_node *cache_ptr; 280 struct cache_node { 281 /* 282 * Index into cache is xid, proc, vers, prog and address 283 */ 284 u_long cache_xid; 285 u_long cache_proc; 286 u_long cache_vers; 287 u_long cache_prog; 288 struct sockaddr_in cache_addr; 289 /* 290 * The cached reply and length 291 */ 292 char * cache_reply; 293 u_long cache_replylen; 294 /* 295 * Next node on the list, if there is a collision 296 */ 297 cache_ptr cache_next; 298 }; 299 300 /* 301 * The entire cache 302 */ 303 struct udp_cache { 304 u_long uc_size; /* size of cache */ 305 cache_ptr *uc_entries; /* hash table of entries in cache */ 306 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 307 u_long uc_nextvictim; /* points to next victim in fifo list */ 308 u_long uc_prog; /* saved program number */ 309 u_long uc_vers; /* saved version number */ 310 u_long uc_proc; /* saved procedure number */ 311 struct sockaddr_in uc_addr; /* saved caller's address */ 312 }; 313 314 315 /* 316 * the hashing function 317 */ 318 #define CACHE_LOC(transp, xid) \ 319 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) 320 321 322 /* 323 * Enable use of the cache. 324 * Note: there is no disable. 325 */ 326 int 327 svcudp_enablecache(SVCXPRT *transp, u_long size) 328 { 329 struct svcudp_data *su = su_data(transp); 330 struct udp_cache *uc; 331 332 if (su->su_cache != NULL) 333 return(0); 334 uc = malloc(sizeof(*uc)); 335 if (uc == NULL) 336 return(0); 337 uc->uc_size = size; 338 uc->uc_nextvictim = 0; 339 if (size > SIZE_MAX / (sizeof(cache_ptr) * SPARSENESS) || 340 (uc->uc_entries = calloc(size, sizeof(cache_ptr) * SPARSENESS)) == NULL) { 341 free(uc); 342 return(0); 343 } 344 uc->uc_fifo = calloc(sizeof(cache_ptr), size); 345 if (uc->uc_fifo == NULL) { 346 free(uc->uc_entries); 347 free(uc); 348 return(0); 349 } 350 su->su_cache = (char *) uc; 351 return(1); 352 } 353 354 355 /* 356 * Set an entry in the cache 357 */ 358 static void 359 cache_set(SVCXPRT *xprt, u_long replylen) 360 { 361 cache_ptr victim; 362 cache_ptr *vicp; 363 struct svcudp_data *su = su_data(xprt); 364 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 365 u_int loc; 366 char *newbuf; 367 368 /* 369 * Find space for the new entry, either by 370 * reusing an old entry, or by mallocing a new one 371 */ 372 victim = uc->uc_fifo[uc->uc_nextvictim]; 373 if (victim != NULL) { 374 loc = CACHE_LOC(xprt, victim->cache_xid); 375 for (vicp = &uc->uc_entries[loc]; 376 *vicp != NULL && *vicp != victim; 377 vicp = &(*vicp)->cache_next) 378 ; 379 if (*vicp == NULL) { 380 return; 381 } 382 *vicp = victim->cache_next; /* remote from cache */ 383 newbuf = victim->cache_reply; 384 } else { 385 victim = malloc(sizeof(struct cache_node)); 386 if (victim == NULL) { 387 return; 388 } 389 newbuf = malloc(su->su_iosz); 390 if (newbuf == NULL) { 391 free(victim); 392 return; 393 } 394 } 395 396 /* 397 * Store it away 398 */ 399 victim->cache_replylen = replylen; 400 victim->cache_reply = rpc_buffer(xprt); 401 rpc_buffer(xprt) = newbuf; 402 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); 403 victim->cache_xid = su->su_xid; 404 victim->cache_proc = uc->uc_proc; 405 victim->cache_vers = uc->uc_vers; 406 victim->cache_prog = uc->uc_prog; 407 victim->cache_addr = uc->uc_addr; 408 loc = CACHE_LOC(xprt, victim->cache_xid); 409 victim->cache_next = uc->uc_entries[loc]; 410 uc->uc_entries[loc] = victim; 411 uc->uc_fifo[uc->uc_nextvictim++] = victim; 412 uc->uc_nextvictim %= uc->uc_size; 413 } 414 415 /* 416 * Try to get an entry from the cache 417 * return 1 if found, 0 if not found 418 */ 419 static int 420 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp) 421 { 422 u_int loc; 423 cache_ptr ent; 424 struct svcudp_data *su = su_data(xprt); 425 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 426 427 # define EQADDR(a1, a2) (memcmp(&a1, &a2, sizeof(a1)) == 0) 428 429 loc = CACHE_LOC(xprt, su->su_xid); 430 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 431 if (ent->cache_xid == su->su_xid && 432 ent->cache_proc == uc->uc_proc && 433 ent->cache_vers == uc->uc_vers && 434 ent->cache_prog == uc->uc_prog && 435 EQADDR(ent->cache_addr, uc->uc_addr)) { 436 *replyp = ent->cache_reply; 437 *replylenp = ent->cache_replylen; 438 return(1); 439 } 440 } 441 /* 442 * Failed to find entry 443 * Remember a few things so we can do a set later 444 */ 445 uc->uc_proc = msg->rm_call.cb_proc; 446 uc->uc_vers = msg->rm_call.cb_vers; 447 uc->uc_prog = msg->rm_call.cb_prog; 448 uc->uc_addr = xprt->xp_raddr; 449 return(0); 450 } 451 452