1 /* $OpenBSD: svc_udp.c,v 1.27 2022/02/14 03:38:59 guenther 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 const 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 *
svcudp_bufcreate(int sock,u_int sendsz,u_int recvsz)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 *
svcudp_create(int sock)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
svcudp_stat(SVCXPRT * xprt)173 svcudp_stat(SVCXPRT *xprt)
174 {
175
176 return (XPRT_IDLE);
177 }
178
179 static bool_t
svcudp_recv(SVCXPRT * xprt,struct rpc_msg * msg)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
svcudp_reply(SVCXPRT * xprt,struct rpc_msg * msg)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
svcudp_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)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
svcudp_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)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
svcudp_destroy(SVCXPRT * xprt)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
svcudp_enablecache(SVCXPRT * transp,u_long size)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
cache_set(SVCXPRT * xprt,u_long replylen)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
cache_get(SVCXPRT * xprt,struct rpc_msg * msg,char ** replyp,u_long * replylenp)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