]>
Commit | Line | Data |
---|---|---|
28f540f4 RM |
1 | /* |
2 | * svc_udp.c, | |
3 | * Server side for UDP/IP based RPC. (Does some caching in the hopes of | |
4 | * achieving execute-at-most-once semantics.) | |
5 | * | |
568035b7 | 6 | * Copyright (C) 2012-2013 Free Software Foundation, Inc. |
14bc93a9 JL |
7 | * This file is part of the GNU C Library. |
8 | * | |
9 | * The GNU C Library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * The GNU C Library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with the GNU C Library; if not, see | |
21 | * <http://www.gnu.org/licenses/>. | |
22 | * | |
a7ab6ec8 UD |
23 | * Copyright (c) 2010, Oracle America, Inc. |
24 | * | |
25 | * Redistribution and use in source and binary forms, with or without | |
26 | * modification, are permitted provided that the following conditions are | |
27 | * met: | |
28 | * | |
29 | * * Redistributions of source code must retain the above copyright | |
30 | * notice, this list of conditions and the following disclaimer. | |
31 | * * Redistributions in binary form must reproduce the above | |
32 | * copyright notice, this list of conditions and the following | |
33 | * disclaimer in the documentation and/or other materials | |
34 | * provided with the distribution. | |
35 | * * Neither the name of the "Oracle America, Inc." nor the names of its | |
36 | * contributors may be used to endorse or promote products derived | |
37 | * from this software without specific prior written permission. | |
38 | * | |
39 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
40 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
41 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
42 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
43 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
44 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
45 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | |
46 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
47 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
48 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
49 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
50 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28f540f4 RM |
51 | */ |
52 | ||
53 | #include <stdio.h> | |
e7fd8a39 UD |
54 | #include <unistd.h> |
55 | #include <string.h> | |
28f540f4 RM |
56 | #include <rpc/rpc.h> |
57 | #include <sys/socket.h> | |
58 | #include <errno.h> | |
4360eafd | 59 | #include <libintl.h> |
28f540f4 | 60 | |
3fd759d1 UD |
61 | #ifdef IP_PKTINFO |
62 | #include <sys/uio.h> | |
63 | #endif | |
64 | ||
3ce1f295 UD |
65 | #include <wchar.h> |
66 | #include <libio/iolibio.h> | |
28f540f4 RM |
67 | |
68 | #define rpc_buffer(xprt) ((xprt)->xp_p1) | |
1f64ac13 | 69 | #ifndef MAX |
e7fd8a39 | 70 | #define MAX(a, b) ((a > b) ? a : b) |
1f64ac13 | 71 | #endif |
28f540f4 | 72 | |
e7fd8a39 UD |
73 | static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *); |
74 | static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *); | |
75 | static enum xprt_stat svcudp_stat (SVCXPRT *); | |
76 | static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t); | |
77 | static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); | |
78 | static void svcudp_destroy (SVCXPRT *); | |
79 | ||
80 | static const struct xp_ops svcudp_op = | |
81 | { | |
82 | svcudp_recv, | |
83 | svcudp_stat, | |
84 | svcudp_getargs, | |
85 | svcudp_reply, | |
86 | svcudp_freeargs, | |
87 | svcudp_destroy | |
28f540f4 RM |
88 | }; |
89 | ||
e7fd8a39 UD |
90 | static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp, |
91 | u_long *replylenp); | |
92 | static void cache_set (SVCXPRT *xprt, u_long replylen); | |
28f540f4 RM |
93 | |
94 | /* | |
95 | * kept in xprt->xp_p2 | |
96 | */ | |
e7fd8a39 UD |
97 | struct svcudp_data |
98 | { | |
99 | u_int su_iosz; /* byte size of send.recv buffer */ | |
100 | u_long su_xid; /* transaction id */ | |
101 | XDR su_xdrs; /* XDR handle */ | |
102 | char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ | |
103 | char *su_cache; /* cached data, NULL if no cache */ | |
104 | }; | |
28f540f4 RM |
105 | #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) |
106 | ||
107 | /* | |
108 | * Usage: | |
e7fd8a39 | 109 | * xprt = svcudp_create(sock); |
28f540f4 RM |
110 | * |
111 | * If sock<0 then a socket is created, else sock is used. | |
112 | * If the socket, sock is not bound to a port then svcudp_create | |
113 | * binds it to an arbitrary port. In any (successful) case, | |
114 | * xprt->xp_sock is the registered socket number and xprt->xp_port is the | |
115 | * associated port number. | |
116 | * Once *xprt is initialized, it is registered as a transporter; | |
117 | * see (svc.h, xprt_register). | |
118 | * The routines returns NULL if a problem occurred. | |
119 | */ | |
120 | SVCXPRT * | |
e7fd8a39 UD |
121 | svcudp_bufcreate (sock, sendsz, recvsz) |
122 | int sock; | |
123 | u_int sendsz, recvsz; | |
28f540f4 | 124 | { |
e7fd8a39 UD |
125 | bool_t madesock = FALSE; |
126 | SVCXPRT *xprt; | |
127 | struct svcudp_data *su; | |
128 | struct sockaddr_in addr; | |
70b0abba | 129 | socklen_t len = sizeof (struct sockaddr_in); |
51028f34 UD |
130 | int pad; |
131 | void *buf; | |
e7fd8a39 UD |
132 | |
133 | if (sock == RPC_ANYSOCK) | |
134 | { | |
50304ef0 | 135 | if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
e7fd8a39 UD |
136 | { |
137 | perror (_("svcudp_create: socket creation problem")); | |
138 | return (SVCXPRT *) NULL; | |
28f540f4 | 139 | } |
e7fd8a39 UD |
140 | madesock = TRUE; |
141 | } | |
50304ef0 | 142 | __bzero ((char *) &addr, sizeof (addr)); |
e7fd8a39 | 143 | addr.sin_family = AF_INET; |
a585ba22 | 144 | if (bindresvport (sock, &addr)) |
e7fd8a39 UD |
145 | { |
146 | addr.sin_port = 0; | |
b2bffca2 | 147 | (void) __bind (sock, (struct sockaddr *) &addr, len); |
e7fd8a39 | 148 | } |
b2bffca2 | 149 | if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0) |
e7fd8a39 UD |
150 | { |
151 | perror (_("svcudp_create - cannot getsockname")); | |
152 | if (madesock) | |
50304ef0 | 153 | (void) __close (sock); |
e7fd8a39 UD |
154 | return (SVCXPRT *) NULL; |
155 | } | |
156 | xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); | |
e7fd8a39 | 157 | su = (struct svcudp_data *) mem_alloc (sizeof (*su)); |
51028f34 UD |
158 | buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4); |
159 | if (xprt == NULL || su == NULL || buf == NULL) | |
e7fd8a39 | 160 | { |
1d20f7f8 UD |
161 | (void) __fxprintf (NULL, "%s: %s", |
162 | "svcudp_create", _("out of memory\n")); | |
2e3e5db6 UD |
163 | mem_free (xprt, sizeof (SVCXPRT)); |
164 | mem_free (su, sizeof (*su)); | |
165 | mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4); | |
e7fd8a39 UD |
166 | return NULL; |
167 | } | |
168 | su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4; | |
51028f34 | 169 | rpc_buffer (xprt) = buf; |
7b57bfe5 | 170 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE); |
e7fd8a39 UD |
171 | su->su_cache = NULL; |
172 | xprt->xp_p2 = (caddr_t) su; | |
173 | xprt->xp_verf.oa_base = su->su_verfbody; | |
174 | xprt->xp_ops = &svcudp_op; | |
175 | xprt->xp_port = ntohs (addr.sin_port); | |
176 | xprt->xp_sock = sock; | |
3fd759d1 UD |
177 | |
178 | #ifdef IP_PKTINFO | |
179 | if ((sizeof (struct iovec) + sizeof (struct msghdr) | |
180 | + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo)) | |
181 | > sizeof (xprt->xp_pad)) | |
182 | { | |
8a259a23 | 183 | (void) __fxprintf (NULL,"%s", _("\ |
df6f8969 | 184 | svcudp_create: xp_pad is too small for IP_PKTINFO\n")); |
3fd759d1 UD |
185 | return NULL; |
186 | } | |
187 | pad = 1; | |
b2bffca2 UD |
188 | if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad, |
189 | sizeof (pad)) == 0) | |
3fd759d1 UD |
190 | /* Set the padding to all 1s. */ |
191 | pad = 0xff; | |
192 | else | |
193 | #endif | |
194 | /* Clear the padding. */ | |
195 | pad = 0; | |
196 | memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad)); | |
197 | ||
e7fd8a39 UD |
198 | xprt_register (xprt); |
199 | return xprt; | |
28f540f4 | 200 | } |
7b57bfe5 UD |
201 | #ifdef EXPORT_RPC_SYMBOLS |
202 | libc_hidden_def (svcudp_bufcreate) | |
203 | #else | |
021db4be | 204 | libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0) |
7b57bfe5 | 205 | #endif |
28f540f4 RM |
206 | |
207 | SVCXPRT * | |
e7fd8a39 UD |
208 | svcudp_create (sock) |
209 | int sock; | |
28f540f4 | 210 | { |
7b57bfe5 | 211 | return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE); |
28f540f4 | 212 | } |
4df46dbd L |
213 | #ifdef EXPORT_RPC_SYMBOLS |
214 | libc_hidden_def (svcudp_create) | |
215 | #else | |
021db4be | 216 | libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0) |
4df46dbd | 217 | #endif |
28f540f4 RM |
218 | |
219 | static enum xprt_stat | |
e7fd8a39 UD |
220 | svcudp_stat (xprt) |
221 | SVCXPRT *xprt; | |
28f540f4 RM |
222 | { |
223 | ||
e7fd8a39 | 224 | return XPRT_IDLE; |
28f540f4 RM |
225 | } |
226 | ||
227 | static bool_t | |
e7fd8a39 UD |
228 | svcudp_recv (xprt, msg) |
229 | SVCXPRT *xprt; | |
230 | struct rpc_msg *msg; | |
28f540f4 | 231 | { |
e7fd8a39 UD |
232 | struct svcudp_data *su = su_data (xprt); |
233 | XDR *xdrs = &(su->su_xdrs); | |
234 | int rlen; | |
235 | char *reply; | |
236 | u_long replylen; | |
70b0abba | 237 | socklen_t len; |
e7fd8a39 | 238 | |
3fd759d1 UD |
239 | /* It is very tricky when you have IP aliases. We want to make sure |
240 | that we are sending the packet from the IP address where the | |
241 | incoming packet is addressed to. H.J. */ | |
242 | #ifdef IP_PKTINFO | |
243 | struct iovec *iovp; | |
244 | struct msghdr *mesgp; | |
245 | #endif | |
246 | ||
e7fd8a39 | 247 | again: |
f671aeab | 248 | /* FIXME -- should xp_addrlen be a size_t? */ |
70b0abba | 249 | len = (socklen_t) sizeof(struct sockaddr_in); |
3fd759d1 UD |
250 | #ifdef IP_PKTINFO |
251 | iovp = (struct iovec *) &xprt->xp_pad [0]; | |
252 | mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)]; | |
253 | if (mesgp->msg_iovlen) | |
254 | { | |
255 | iovp->iov_base = rpc_buffer (xprt); | |
256 | iovp->iov_len = su->su_iosz; | |
257 | mesgp->msg_iov = iovp; | |
258 | mesgp->msg_iovlen = 1; | |
259 | mesgp->msg_name = &(xprt->xp_raddr); | |
260 | mesgp->msg_namelen = len; | |
261 | mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec) | |
262 | + sizeof (struct msghdr)]; | |
172b90bb UD |
263 | mesgp->msg_controllen = sizeof(xprt->xp_pad) |
264 | - sizeof (struct iovec) - sizeof (struct msghdr); | |
b2bffca2 | 265 | rlen = __recvmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 | 266 | if (rlen >= 0) |
d148ed25 UD |
267 | { |
268 | struct cmsghdr *cmsg; | |
269 | len = mesgp->msg_namelen; | |
270 | cmsg = CMSG_FIRSTHDR (mesgp); | |
271 | if (cmsg == NULL | |
272 | || CMSG_NXTHDR (mesgp, cmsg) != NULL | |
273 | || cmsg->cmsg_level != SOL_IP | |
274 | || cmsg->cmsg_type != IP_PKTINFO | |
275 | || cmsg->cmsg_len < (sizeof (struct cmsghdr) | |
276 | + sizeof (struct in_pktinfo))) | |
277 | { | |
278 | /* Not a simple IP_PKTINFO, ignore it. */ | |
279 | mesgp->msg_control = NULL; | |
280 | mesgp->msg_controllen = 0; | |
281 | } | |
282 | else | |
283 | { | |
284 | /* It was a simple IP_PKTIFO as we expected, discard the | |
285 | interface field. */ | |
63c9fb5c | 286 | struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); |
d148ed25 UD |
287 | pkti->ipi_ifindex = 0; |
288 | } | |
289 | } | |
3fd759d1 UD |
290 | } |
291 | else | |
292 | #endif | |
b2bffca2 UD |
293 | rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt), |
294 | (int) su->su_iosz, 0, | |
295 | (struct sockaddr *) &(xprt->xp_raddr), &len); | |
f671aeab | 296 | xprt->xp_addrlen = len; |
14bc93a9 JL |
297 | if (rlen == -1) |
298 | { | |
299 | if (errno == EINTR) | |
300 | goto again; | |
301 | __svc_accept_failed (); | |
302 | } | |
e7fd8a39 UD |
303 | if (rlen < 16) /* < 4 32-bit ints? */ |
304 | return FALSE; | |
305 | xdrs->x_op = XDR_DECODE; | |
306 | XDR_SETPOS (xdrs, 0); | |
7b57bfe5 | 307 | if (!xdr_callmsg (xdrs, msg)) |
e7fd8a39 UD |
308 | return FALSE; |
309 | su->su_xid = msg->rm_xid; | |
310 | if (su->su_cache != NULL) | |
311 | { | |
312 | if (cache_get (xprt, msg, &reply, &replylen)) | |
313 | { | |
3fd759d1 UD |
314 | #ifdef IP_PKTINFO |
315 | if (mesgp->msg_iovlen) | |
316 | { | |
317 | iovp->iov_base = reply; | |
318 | iovp->iov_len = replylen; | |
b2bffca2 | 319 | (void) __sendmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 UD |
320 | } |
321 | else | |
322 | #endif | |
b2bffca2 UD |
323 | (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0, |
324 | (struct sockaddr *) &xprt->xp_raddr, len); | |
e7fd8a39 | 325 | return TRUE; |
28f540f4 | 326 | } |
e7fd8a39 UD |
327 | } |
328 | return TRUE; | |
28f540f4 RM |
329 | } |
330 | ||
331 | static bool_t | |
e7fd8a39 UD |
332 | svcudp_reply (xprt, msg) |
333 | SVCXPRT *xprt; | |
334 | struct rpc_msg *msg; | |
28f540f4 | 335 | { |
e7fd8a39 UD |
336 | struct svcudp_data *su = su_data (xprt); |
337 | XDR *xdrs = &(su->su_xdrs); | |
3fd759d1 | 338 | int slen, sent; |
e7fd8a39 | 339 | bool_t stat = FALSE; |
3fd759d1 UD |
340 | #ifdef IP_PKTINFO |
341 | struct iovec *iovp; | |
342 | struct msghdr *mesgp; | |
343 | #endif | |
e7fd8a39 UD |
344 | |
345 | xdrs->x_op = XDR_ENCODE; | |
346 | XDR_SETPOS (xdrs, 0); | |
347 | msg->rm_xid = su->su_xid; | |
7b57bfe5 | 348 | if (xdr_replymsg (xdrs, msg)) |
e7fd8a39 UD |
349 | { |
350 | slen = (int) XDR_GETPOS (xdrs); | |
3fd759d1 UD |
351 | #ifdef IP_PKTINFO |
352 | mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)]; | |
353 | if (mesgp->msg_iovlen) | |
354 | { | |
355 | iovp = (struct iovec *) &xprt->xp_pad [0]; | |
356 | iovp->iov_base = rpc_buffer (xprt); | |
357 | iovp->iov_len = slen; | |
b2bffca2 | 358 | sent = __sendmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 UD |
359 | } |
360 | else | |
361 | #endif | |
b2bffca2 UD |
362 | sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0, |
363 | (struct sockaddr *) &(xprt->xp_raddr), | |
364 | xprt->xp_addrlen); | |
3fd759d1 | 365 | if (sent == slen) |
e7fd8a39 UD |
366 | { |
367 | stat = TRUE; | |
368 | if (su->su_cache && slen >= 0) | |
369 | { | |
370 | cache_set (xprt, (u_long) slen); | |
371 | } | |
28f540f4 | 372 | } |
e7fd8a39 UD |
373 | } |
374 | return stat; | |
28f540f4 RM |
375 | } |
376 | ||
377 | static bool_t | |
e7fd8a39 UD |
378 | svcudp_getargs (xprt, xdr_args, args_ptr) |
379 | SVCXPRT *xprt; | |
380 | xdrproc_t xdr_args; | |
381 | caddr_t args_ptr; | |
28f540f4 RM |
382 | { |
383 | ||
e7fd8a39 | 384 | return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr); |
28f540f4 RM |
385 | } |
386 | ||
387 | static bool_t | |
e7fd8a39 UD |
388 | svcudp_freeargs (xprt, xdr_args, args_ptr) |
389 | SVCXPRT *xprt; | |
390 | xdrproc_t xdr_args; | |
391 | caddr_t args_ptr; | |
28f540f4 | 392 | { |
e7fd8a39 | 393 | XDR *xdrs = &(su_data (xprt)->su_xdrs); |
28f540f4 | 394 | |
e7fd8a39 UD |
395 | xdrs->x_op = XDR_FREE; |
396 | return (*xdr_args) (xdrs, args_ptr); | |
28f540f4 RM |
397 | } |
398 | ||
399 | static void | |
e7fd8a39 UD |
400 | svcudp_destroy (xprt) |
401 | SVCXPRT *xprt; | |
28f540f4 | 402 | { |
e7fd8a39 UD |
403 | struct svcudp_data *su = su_data (xprt); |
404 | ||
405 | xprt_unregister (xprt); | |
50304ef0 | 406 | (void) __close (xprt->xp_sock); |
e7fd8a39 UD |
407 | XDR_DESTROY (&(su->su_xdrs)); |
408 | mem_free (rpc_buffer (xprt), su->su_iosz); | |
409 | mem_free ((caddr_t) su, sizeof (struct svcudp_data)); | |
410 | mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); | |
28f540f4 RM |
411 | } |
412 | ||
413 | ||
414 | /***********this could be a separate file*********************/ | |
415 | ||
416 | /* | |
417 | * Fifo cache for udp server | |
418 | * Copies pointers to reply buffers into fifo cache | |
419 | * Buffers are sent again if retransmissions are detected. | |
420 | */ | |
421 | ||
e7fd8a39 | 422 | #define SPARSENESS 4 /* 75% sparse */ |
28f540f4 | 423 | |
df6f8969 | 424 | #define CACHE_PERROR(msg) \ |
8a259a23 | 425 | (void) __fxprintf(NULL, "%s\n", msg) |
28f540f4 RM |
426 | |
427 | #define ALLOC(type, size) \ | |
428 | (type *) mem_alloc((unsigned) (sizeof(type) * (size))) | |
429 | ||
cdb9c321 UD |
430 | #define CALLOC(type, size) \ |
431 | (type *) calloc (sizeof (type), size) | |
28f540f4 RM |
432 | |
433 | /* | |
434 | * An entry in the cache | |
435 | */ | |
436 | typedef struct cache_node *cache_ptr; | |
e7fd8a39 UD |
437 | struct cache_node |
438 | { | |
439 | /* | |
440 | * Index into cache is xid, proc, vers, prog and address | |
441 | */ | |
442 | u_long cache_xid; | |
443 | u_long cache_proc; | |
444 | u_long cache_vers; | |
445 | u_long cache_prog; | |
446 | struct sockaddr_in cache_addr; | |
447 | /* | |
448 | * The cached reply and length | |
449 | */ | |
450 | char *cache_reply; | |
451 | u_long cache_replylen; | |
452 | /* | |
453 | * Next node on the list, if there is a collision | |
454 | */ | |
455 | cache_ptr cache_next; | |
456 | }; | |
28f540f4 RM |
457 | |
458 | ||
459 | ||
460 | /* | |
461 | * The entire cache | |
462 | */ | |
e7fd8a39 UD |
463 | struct udp_cache |
464 | { | |
465 | u_long uc_size; /* size of cache */ | |
466 | cache_ptr *uc_entries; /* hash table of entries in cache */ | |
467 | cache_ptr *uc_fifo; /* fifo list of entries in cache */ | |
468 | u_long uc_nextvictim; /* points to next victim in fifo list */ | |
469 | u_long uc_prog; /* saved program number */ | |
470 | u_long uc_vers; /* saved version number */ | |
471 | u_long uc_proc; /* saved procedure number */ | |
472 | struct sockaddr_in uc_addr; /* saved caller's address */ | |
473 | }; | |
28f540f4 RM |
474 | |
475 | ||
476 | /* | |
477 | * the hashing function | |
478 | */ | |
479 | #define CACHE_LOC(transp, xid) \ | |
cbd3dceb | 480 | (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) |
28f540f4 RM |
481 | |
482 | ||
483 | /* | |
cbd3dceb | 484 | * Enable use of the cache. |
28f540f4 RM |
485 | * Note: there is no disable. |
486 | */ | |
e7fd8a39 UD |
487 | int |
488 | svcudp_enablecache (SVCXPRT *transp, u_long size) | |
28f540f4 | 489 | { |
e7fd8a39 UD |
490 | struct svcudp_data *su = su_data (transp); |
491 | struct udp_cache *uc; | |
492 | ||
493 | if (su->su_cache != NULL) | |
494 | { | |
495 | CACHE_PERROR (_("enablecache: cache already enabled")); | |
496 | return 0; | |
497 | } | |
498 | uc = ALLOC (struct udp_cache, 1); | |
499 | if (uc == NULL) | |
500 | { | |
501 | CACHE_PERROR (_("enablecache: could not allocate cache")); | |
502 | return 0; | |
503 | } | |
504 | uc->uc_size = size; | |
505 | uc->uc_nextvictim = 0; | |
cdb9c321 | 506 | uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS); |
e7fd8a39 UD |
507 | if (uc->uc_entries == NULL) |
508 | { | |
0292b0dd | 509 | mem_free (uc, sizeof (struct udp_cache)); |
e7fd8a39 UD |
510 | CACHE_PERROR (_("enablecache: could not allocate cache data")); |
511 | return 0; | |
512 | } | |
cdb9c321 | 513 | uc->uc_fifo = CALLOC (cache_ptr, size); |
e7fd8a39 UD |
514 | if (uc->uc_fifo == NULL) |
515 | { | |
0292b0dd UD |
516 | mem_free (uc->uc_entries, size * SPARSENESS); |
517 | mem_free (uc, sizeof (struct udp_cache)); | |
e7fd8a39 UD |
518 | CACHE_PERROR (_("enablecache: could not allocate cache fifo")); |
519 | return 0; | |
520 | } | |
e7fd8a39 UD |
521 | su->su_cache = (char *) uc; |
522 | return 1; | |
28f540f4 | 523 | } |
021db4be | 524 | libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0) |
28f540f4 RM |
525 | |
526 | ||
527 | /* | |
528 | * Set an entry in the cache | |
529 | */ | |
e7fd8a39 UD |
530 | static void |
531 | cache_set (SVCXPRT *xprt, u_long replylen) | |
28f540f4 | 532 | { |
e7fd8a39 UD |
533 | cache_ptr victim; |
534 | cache_ptr *vicp; | |
535 | struct svcudp_data *su = su_data (xprt); | |
536 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
537 | u_int loc; | |
538 | char *newbuf; | |
539 | ||
540 | /* | |
541 | * Find space for the new entry, either by | |
542 | * reusing an old entry, or by mallocing a new one | |
543 | */ | |
544 | victim = uc->uc_fifo[uc->uc_nextvictim]; | |
545 | if (victim != NULL) | |
546 | { | |
547 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
548 | for (vicp = &uc->uc_entries[loc]; | |
549 | *vicp != NULL && *vicp != victim; | |
550 | vicp = &(*vicp)->cache_next) | |
551 | ; | |
552 | if (*vicp == NULL) | |
553 | { | |
554 | CACHE_PERROR (_("cache_set: victim not found")); | |
555 | return; | |
28f540f4 | 556 | } |
e7fd8a39 UD |
557 | *vicp = victim->cache_next; /* remote from cache */ |
558 | newbuf = victim->cache_reply; | |
559 | } | |
560 | else | |
561 | { | |
562 | victim = ALLOC (struct cache_node, 1); | |
563 | if (victim == NULL) | |
564 | { | |
565 | CACHE_PERROR (_("cache_set: victim alloc failed")); | |
566 | return; | |
567 | } | |
568 | newbuf = mem_alloc (su->su_iosz); | |
569 | if (newbuf == NULL) | |
570 | { | |
0292b0dd | 571 | mem_free (victim, sizeof (struct cache_node)); |
e7fd8a39 UD |
572 | CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer")); |
573 | return; | |
574 | } | |
575 | } | |
576 | ||
577 | /* | |
578 | * Store it away | |
579 | */ | |
580 | victim->cache_replylen = replylen; | |
581 | victim->cache_reply = rpc_buffer (xprt); | |
582 | rpc_buffer (xprt) = newbuf; | |
7b57bfe5 | 583 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE); |
e7fd8a39 UD |
584 | victim->cache_xid = su->su_xid; |
585 | victim->cache_proc = uc->uc_proc; | |
586 | victim->cache_vers = uc->uc_vers; | |
587 | victim->cache_prog = uc->uc_prog; | |
588 | victim->cache_addr = uc->uc_addr; | |
589 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
590 | victim->cache_next = uc->uc_entries[loc]; | |
591 | uc->uc_entries[loc] = victim; | |
592 | uc->uc_fifo[uc->uc_nextvictim++] = victim; | |
593 | uc->uc_nextvictim %= uc->uc_size; | |
28f540f4 RM |
594 | } |
595 | ||
596 | /* | |
597 | * Try to get an entry from the cache | |
598 | * return 1 if found, 0 if not found | |
599 | */ | |
e7fd8a39 UD |
600 | static int |
601 | cache_get (xprt, msg, replyp, replylenp) | |
602 | SVCXPRT *xprt; | |
603 | struct rpc_msg *msg; | |
604 | char **replyp; | |
605 | u_long *replylenp; | |
28f540f4 | 606 | { |
e7fd8a39 UD |
607 | u_int loc; |
608 | cache_ptr ent; | |
609 | struct svcudp_data *su = su_data (xprt); | |
610 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
611 | ||
50304ef0 | 612 | #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) |
e7fd8a39 UD |
613 | |
614 | loc = CACHE_LOC (xprt, su->su_xid); | |
615 | for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) | |
616 | { | |
617 | if (ent->cache_xid == su->su_xid && | |
618 | ent->cache_proc == uc->uc_proc && | |
619 | ent->cache_vers == uc->uc_vers && | |
620 | ent->cache_prog == uc->uc_prog && | |
621 | EQADDR (ent->cache_addr, uc->uc_addr)) | |
622 | { | |
623 | *replyp = ent->cache_reply; | |
624 | *replylenp = ent->cache_replylen; | |
625 | return 1; | |
28f540f4 | 626 | } |
e7fd8a39 UD |
627 | } |
628 | /* | |
629 | * Failed to find entry | |
630 | * Remember a few things so we can do a set later | |
631 | */ | |
632 | uc->uc_proc = msg->rm_call.cb_proc; | |
633 | uc->uc_vers = msg->rm_call.cb_vers; | |
634 | uc->uc_prog = msg->rm_call.cb_prog; | |
c1301d9a | 635 | memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr)); |
e7fd8a39 | 636 | return 0; |
28f540f4 | 637 | } |