]>
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 | * | |
04277e02 | 6 | * Copyright (C) 2012-2019 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> | |
82f43dd2 | 67 | #include <shlib-compat.h> |
28f540f4 RM |
68 | |
69 | #define rpc_buffer(xprt) ((xprt)->xp_p1) | |
1f64ac13 | 70 | #ifndef MAX |
e7fd8a39 | 71 | #define MAX(a, b) ((a > b) ? a : b) |
1f64ac13 | 72 | #endif |
28f540f4 | 73 | |
e7fd8a39 UD |
74 | static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *); |
75 | static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *); | |
76 | static enum xprt_stat svcudp_stat (SVCXPRT *); | |
77 | static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t); | |
78 | static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); | |
79 | static void svcudp_destroy (SVCXPRT *); | |
80 | ||
81 | static const struct xp_ops svcudp_op = | |
82 | { | |
83 | svcudp_recv, | |
84 | svcudp_stat, | |
85 | svcudp_getargs, | |
86 | svcudp_reply, | |
87 | svcudp_freeargs, | |
88 | svcudp_destroy | |
28f540f4 RM |
89 | }; |
90 | ||
e7fd8a39 UD |
91 | static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp, |
92 | u_long *replylenp); | |
93 | static void cache_set (SVCXPRT *xprt, u_long replylen); | |
28f540f4 RM |
94 | |
95 | /* | |
96 | * kept in xprt->xp_p2 | |
97 | */ | |
e7fd8a39 UD |
98 | struct svcudp_data |
99 | { | |
100 | u_int su_iosz; /* byte size of send.recv buffer */ | |
101 | u_long su_xid; /* transaction id */ | |
102 | XDR su_xdrs; /* XDR handle */ | |
103 | char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ | |
104 | char *su_cache; /* cached data, NULL if no cache */ | |
105 | }; | |
28f540f4 RM |
106 | #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) |
107 | ||
108 | /* | |
109 | * Usage: | |
e7fd8a39 | 110 | * xprt = svcudp_create(sock); |
28f540f4 RM |
111 | * |
112 | * If sock<0 then a socket is created, else sock is used. | |
113 | * If the socket, sock is not bound to a port then svcudp_create | |
114 | * binds it to an arbitrary port. In any (successful) case, | |
115 | * xprt->xp_sock is the registered socket number and xprt->xp_port is the | |
116 | * associated port number. | |
117 | * Once *xprt is initialized, it is registered as a transporter; | |
118 | * see (svc.h, xprt_register). | |
119 | * The routines returns NULL if a problem occurred. | |
120 | */ | |
121 | SVCXPRT * | |
41075ae3 | 122 | svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz) |
28f540f4 | 123 | { |
e7fd8a39 UD |
124 | bool_t madesock = FALSE; |
125 | SVCXPRT *xprt; | |
126 | struct svcudp_data *su; | |
127 | struct sockaddr_in addr; | |
70b0abba | 128 | socklen_t len = sizeof (struct sockaddr_in); |
51028f34 UD |
129 | int pad; |
130 | void *buf; | |
e7fd8a39 UD |
131 | |
132 | if (sock == RPC_ANYSOCK) | |
133 | { | |
50304ef0 | 134 | if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
e7fd8a39 UD |
135 | { |
136 | perror (_("svcudp_create: socket creation problem")); | |
137 | return (SVCXPRT *) NULL; | |
28f540f4 | 138 | } |
e7fd8a39 UD |
139 | madesock = TRUE; |
140 | } | |
d99431e5 | 141 | memset ((char *) &addr, 0, sizeof (addr)); |
e7fd8a39 | 142 | addr.sin_family = AF_INET; |
a585ba22 | 143 | if (bindresvport (sock, &addr)) |
e7fd8a39 UD |
144 | { |
145 | addr.sin_port = 0; | |
b2bffca2 | 146 | (void) __bind (sock, (struct sockaddr *) &addr, len); |
e7fd8a39 | 147 | } |
b2bffca2 | 148 | if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0) |
e7fd8a39 UD |
149 | { |
150 | perror (_("svcudp_create - cannot getsockname")); | |
151 | if (madesock) | |
50304ef0 | 152 | (void) __close (sock); |
e7fd8a39 UD |
153 | return (SVCXPRT *) NULL; |
154 | } | |
155 | xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); | |
e7fd8a39 | 156 | su = (struct svcudp_data *) mem_alloc (sizeof (*su)); |
51028f34 UD |
157 | buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4); |
158 | if (xprt == NULL || su == NULL || buf == NULL) | |
e7fd8a39 | 159 | { |
1d20f7f8 UD |
160 | (void) __fxprintf (NULL, "%s: %s", |
161 | "svcudp_create", _("out of memory\n")); | |
2e3e5db6 UD |
162 | mem_free (xprt, sizeof (SVCXPRT)); |
163 | mem_free (su, sizeof (*su)); | |
164 | mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4); | |
e7fd8a39 UD |
165 | return NULL; |
166 | } | |
167 | su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4; | |
51028f34 | 168 | rpc_buffer (xprt) = buf; |
7b57bfe5 | 169 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE); |
e7fd8a39 UD |
170 | su->su_cache = NULL; |
171 | xprt->xp_p2 = (caddr_t) su; | |
172 | xprt->xp_verf.oa_base = su->su_verfbody; | |
173 | xprt->xp_ops = &svcudp_op; | |
174 | xprt->xp_port = ntohs (addr.sin_port); | |
175 | xprt->xp_sock = sock; | |
3fd759d1 UD |
176 | |
177 | #ifdef IP_PKTINFO | |
178 | if ((sizeof (struct iovec) + sizeof (struct msghdr) | |
179 | + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo)) | |
180 | > sizeof (xprt->xp_pad)) | |
181 | { | |
8a259a23 | 182 | (void) __fxprintf (NULL,"%s", _("\ |
df6f8969 | 183 | svcudp_create: xp_pad is too small for IP_PKTINFO\n")); |
3fd759d1 UD |
184 | return NULL; |
185 | } | |
186 | pad = 1; | |
b2bffca2 UD |
187 | if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad, |
188 | sizeof (pad)) == 0) | |
3fd759d1 UD |
189 | /* Set the padding to all 1s. */ |
190 | pad = 0xff; | |
191 | else | |
192 | #endif | |
193 | /* Clear the padding. */ | |
194 | pad = 0; | |
195 | memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad)); | |
196 | ||
e7fd8a39 UD |
197 | xprt_register (xprt); |
198 | return xprt; | |
28f540f4 | 199 | } |
7b57bfe5 UD |
200 | #ifdef EXPORT_RPC_SYMBOLS |
201 | libc_hidden_def (svcudp_bufcreate) | |
202 | #else | |
021db4be | 203 | libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0) |
7b57bfe5 | 204 | #endif |
28f540f4 RM |
205 | |
206 | SVCXPRT * | |
9d46370c | 207 | svcudp_create (int sock) |
28f540f4 | 208 | { |
7b57bfe5 | 209 | return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE); |
28f540f4 | 210 | } |
4df46dbd L |
211 | #ifdef EXPORT_RPC_SYMBOLS |
212 | libc_hidden_def (svcudp_create) | |
213 | #else | |
021db4be | 214 | libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0) |
4df46dbd | 215 | #endif |
28f540f4 RM |
216 | |
217 | static enum xprt_stat | |
9d46370c | 218 | svcudp_stat (SVCXPRT *xprt) |
28f540f4 RM |
219 | { |
220 | ||
e7fd8a39 | 221 | return XPRT_IDLE; |
28f540f4 RM |
222 | } |
223 | ||
224 | static bool_t | |
9d46370c | 225 | svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg) |
28f540f4 | 226 | { |
e7fd8a39 UD |
227 | struct svcudp_data *su = su_data (xprt); |
228 | XDR *xdrs = &(su->su_xdrs); | |
229 | int rlen; | |
230 | char *reply; | |
231 | u_long replylen; | |
70b0abba | 232 | socklen_t len; |
e7fd8a39 | 233 | |
3fd759d1 UD |
234 | /* It is very tricky when you have IP aliases. We want to make sure |
235 | that we are sending the packet from the IP address where the | |
236 | incoming packet is addressed to. H.J. */ | |
237 | #ifdef IP_PKTINFO | |
238 | struct iovec *iovp; | |
239 | struct msghdr *mesgp; | |
240 | #endif | |
241 | ||
e7fd8a39 | 242 | again: |
f671aeab | 243 | /* FIXME -- should xp_addrlen be a size_t? */ |
70b0abba | 244 | len = (socklen_t) sizeof(struct sockaddr_in); |
3fd759d1 UD |
245 | #ifdef IP_PKTINFO |
246 | iovp = (struct iovec *) &xprt->xp_pad [0]; | |
247 | mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)]; | |
248 | if (mesgp->msg_iovlen) | |
249 | { | |
250 | iovp->iov_base = rpc_buffer (xprt); | |
251 | iovp->iov_len = su->su_iosz; | |
252 | mesgp->msg_iov = iovp; | |
253 | mesgp->msg_iovlen = 1; | |
254 | mesgp->msg_name = &(xprt->xp_raddr); | |
255 | mesgp->msg_namelen = len; | |
256 | mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec) | |
257 | + sizeof (struct msghdr)]; | |
172b90bb UD |
258 | mesgp->msg_controllen = sizeof(xprt->xp_pad) |
259 | - sizeof (struct iovec) - sizeof (struct msghdr); | |
b2bffca2 | 260 | rlen = __recvmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 | 261 | if (rlen >= 0) |
d148ed25 UD |
262 | { |
263 | struct cmsghdr *cmsg; | |
264 | len = mesgp->msg_namelen; | |
265 | cmsg = CMSG_FIRSTHDR (mesgp); | |
266 | if (cmsg == NULL | |
267 | || CMSG_NXTHDR (mesgp, cmsg) != NULL | |
268 | || cmsg->cmsg_level != SOL_IP | |
269 | || cmsg->cmsg_type != IP_PKTINFO | |
270 | || cmsg->cmsg_len < (sizeof (struct cmsghdr) | |
271 | + sizeof (struct in_pktinfo))) | |
272 | { | |
273 | /* Not a simple IP_PKTINFO, ignore it. */ | |
274 | mesgp->msg_control = NULL; | |
275 | mesgp->msg_controllen = 0; | |
276 | } | |
277 | else | |
278 | { | |
279 | /* It was a simple IP_PKTIFO as we expected, discard the | |
280 | interface field. */ | |
63c9fb5c | 281 | struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); |
d148ed25 UD |
282 | pkti->ipi_ifindex = 0; |
283 | } | |
284 | } | |
3fd759d1 UD |
285 | } |
286 | else | |
287 | #endif | |
b2bffca2 UD |
288 | rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt), |
289 | (int) su->su_iosz, 0, | |
290 | (struct sockaddr *) &(xprt->xp_raddr), &len); | |
f671aeab | 291 | xprt->xp_addrlen = len; |
14bc93a9 JL |
292 | if (rlen == -1) |
293 | { | |
294 | if (errno == EINTR) | |
295 | goto again; | |
296 | __svc_accept_failed (); | |
297 | } | |
e7fd8a39 UD |
298 | if (rlen < 16) /* < 4 32-bit ints? */ |
299 | return FALSE; | |
300 | xdrs->x_op = XDR_DECODE; | |
301 | XDR_SETPOS (xdrs, 0); | |
7b57bfe5 | 302 | if (!xdr_callmsg (xdrs, msg)) |
e7fd8a39 UD |
303 | return FALSE; |
304 | su->su_xid = msg->rm_xid; | |
305 | if (su->su_cache != NULL) | |
306 | { | |
307 | if (cache_get (xprt, msg, &reply, &replylen)) | |
308 | { | |
3fd759d1 UD |
309 | #ifdef IP_PKTINFO |
310 | if (mesgp->msg_iovlen) | |
311 | { | |
312 | iovp->iov_base = reply; | |
313 | iovp->iov_len = replylen; | |
b2bffca2 | 314 | (void) __sendmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 UD |
315 | } |
316 | else | |
317 | #endif | |
b2bffca2 UD |
318 | (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0, |
319 | (struct sockaddr *) &xprt->xp_raddr, len); | |
e7fd8a39 | 320 | return TRUE; |
28f540f4 | 321 | } |
e7fd8a39 UD |
322 | } |
323 | return TRUE; | |
28f540f4 RM |
324 | } |
325 | ||
326 | static bool_t | |
9d46370c | 327 | svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg) |
28f540f4 | 328 | { |
e7fd8a39 UD |
329 | struct svcudp_data *su = su_data (xprt); |
330 | XDR *xdrs = &(su->su_xdrs); | |
3fd759d1 | 331 | int slen, sent; |
e7fd8a39 | 332 | bool_t stat = FALSE; |
3fd759d1 UD |
333 | #ifdef IP_PKTINFO |
334 | struct iovec *iovp; | |
335 | struct msghdr *mesgp; | |
336 | #endif | |
e7fd8a39 UD |
337 | |
338 | xdrs->x_op = XDR_ENCODE; | |
339 | XDR_SETPOS (xdrs, 0); | |
340 | msg->rm_xid = su->su_xid; | |
7b57bfe5 | 341 | if (xdr_replymsg (xdrs, msg)) |
e7fd8a39 UD |
342 | { |
343 | slen = (int) XDR_GETPOS (xdrs); | |
3fd759d1 UD |
344 | #ifdef IP_PKTINFO |
345 | mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)]; | |
346 | if (mesgp->msg_iovlen) | |
347 | { | |
348 | iovp = (struct iovec *) &xprt->xp_pad [0]; | |
349 | iovp->iov_base = rpc_buffer (xprt); | |
350 | iovp->iov_len = slen; | |
b2bffca2 | 351 | sent = __sendmsg (xprt->xp_sock, mesgp, 0); |
3fd759d1 UD |
352 | } |
353 | else | |
354 | #endif | |
b2bffca2 UD |
355 | sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0, |
356 | (struct sockaddr *) &(xprt->xp_raddr), | |
357 | xprt->xp_addrlen); | |
3fd759d1 | 358 | if (sent == slen) |
e7fd8a39 UD |
359 | { |
360 | stat = TRUE; | |
361 | if (su->su_cache && slen >= 0) | |
362 | { | |
363 | cache_set (xprt, (u_long) slen); | |
364 | } | |
28f540f4 | 365 | } |
e7fd8a39 UD |
366 | } |
367 | return stat; | |
28f540f4 RM |
368 | } |
369 | ||
370 | static bool_t | |
9d46370c | 371 | svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) |
28f540f4 RM |
372 | { |
373 | ||
e7fd8a39 | 374 | return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr); |
28f540f4 RM |
375 | } |
376 | ||
377 | static bool_t | |
9d46370c | 378 | svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) |
28f540f4 | 379 | { |
e7fd8a39 | 380 | XDR *xdrs = &(su_data (xprt)->su_xdrs); |
28f540f4 | 381 | |
e7fd8a39 UD |
382 | xdrs->x_op = XDR_FREE; |
383 | return (*xdr_args) (xdrs, args_ptr); | |
28f540f4 RM |
384 | } |
385 | ||
386 | static void | |
9d46370c | 387 | svcudp_destroy (SVCXPRT *xprt) |
28f540f4 | 388 | { |
e7fd8a39 UD |
389 | struct svcudp_data *su = su_data (xprt); |
390 | ||
391 | xprt_unregister (xprt); | |
50304ef0 | 392 | (void) __close (xprt->xp_sock); |
e7fd8a39 UD |
393 | XDR_DESTROY (&(su->su_xdrs)); |
394 | mem_free (rpc_buffer (xprt), su->su_iosz); | |
395 | mem_free ((caddr_t) su, sizeof (struct svcudp_data)); | |
396 | mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); | |
28f540f4 RM |
397 | } |
398 | ||
399 | ||
400 | /***********this could be a separate file*********************/ | |
401 | ||
402 | /* | |
403 | * Fifo cache for udp server | |
404 | * Copies pointers to reply buffers into fifo cache | |
405 | * Buffers are sent again if retransmissions are detected. | |
406 | */ | |
407 | ||
e7fd8a39 | 408 | #define SPARSENESS 4 /* 75% sparse */ |
28f540f4 | 409 | |
df6f8969 | 410 | #define CACHE_PERROR(msg) \ |
8a259a23 | 411 | (void) __fxprintf(NULL, "%s\n", msg) |
28f540f4 RM |
412 | |
413 | #define ALLOC(type, size) \ | |
414 | (type *) mem_alloc((unsigned) (sizeof(type) * (size))) | |
415 | ||
cdb9c321 UD |
416 | #define CALLOC(type, size) \ |
417 | (type *) calloc (sizeof (type), size) | |
28f540f4 RM |
418 | |
419 | /* | |
420 | * An entry in the cache | |
421 | */ | |
422 | typedef struct cache_node *cache_ptr; | |
e7fd8a39 UD |
423 | struct cache_node |
424 | { | |
425 | /* | |
426 | * Index into cache is xid, proc, vers, prog and address | |
427 | */ | |
428 | u_long cache_xid; | |
429 | u_long cache_proc; | |
430 | u_long cache_vers; | |
431 | u_long cache_prog; | |
432 | struct sockaddr_in cache_addr; | |
433 | /* | |
434 | * The cached reply and length | |
435 | */ | |
436 | char *cache_reply; | |
437 | u_long cache_replylen; | |
438 | /* | |
439 | * Next node on the list, if there is a collision | |
440 | */ | |
441 | cache_ptr cache_next; | |
442 | }; | |
28f540f4 RM |
443 | |
444 | ||
445 | ||
446 | /* | |
447 | * The entire cache | |
448 | */ | |
e7fd8a39 UD |
449 | struct udp_cache |
450 | { | |
451 | u_long uc_size; /* size of cache */ | |
452 | cache_ptr *uc_entries; /* hash table of entries in cache */ | |
453 | cache_ptr *uc_fifo; /* fifo list of entries in cache */ | |
454 | u_long uc_nextvictim; /* points to next victim in fifo list */ | |
455 | u_long uc_prog; /* saved program number */ | |
456 | u_long uc_vers; /* saved version number */ | |
457 | u_long uc_proc; /* saved procedure number */ | |
458 | struct sockaddr_in uc_addr; /* saved caller's address */ | |
459 | }; | |
28f540f4 RM |
460 | |
461 | ||
462 | /* | |
463 | * the hashing function | |
464 | */ | |
465 | #define CACHE_LOC(transp, xid) \ | |
cbd3dceb | 466 | (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) |
28f540f4 RM |
467 | |
468 | ||
469 | /* | |
cbd3dceb | 470 | * Enable use of the cache. |
28f540f4 RM |
471 | * Note: there is no disable. |
472 | */ | |
e7fd8a39 UD |
473 | int |
474 | svcudp_enablecache (SVCXPRT *transp, u_long size) | |
28f540f4 | 475 | { |
e7fd8a39 UD |
476 | struct svcudp_data *su = su_data (transp); |
477 | struct udp_cache *uc; | |
478 | ||
479 | if (su->su_cache != NULL) | |
480 | { | |
481 | CACHE_PERROR (_("enablecache: cache already enabled")); | |
482 | return 0; | |
483 | } | |
484 | uc = ALLOC (struct udp_cache, 1); | |
485 | if (uc == NULL) | |
486 | { | |
487 | CACHE_PERROR (_("enablecache: could not allocate cache")); | |
488 | return 0; | |
489 | } | |
490 | uc->uc_size = size; | |
491 | uc->uc_nextvictim = 0; | |
cdb9c321 | 492 | uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS); |
e7fd8a39 UD |
493 | if (uc->uc_entries == NULL) |
494 | { | |
0292b0dd | 495 | mem_free (uc, sizeof (struct udp_cache)); |
e7fd8a39 UD |
496 | CACHE_PERROR (_("enablecache: could not allocate cache data")); |
497 | return 0; | |
498 | } | |
cdb9c321 | 499 | uc->uc_fifo = CALLOC (cache_ptr, size); |
e7fd8a39 UD |
500 | if (uc->uc_fifo == NULL) |
501 | { | |
0292b0dd UD |
502 | mem_free (uc->uc_entries, size * SPARSENESS); |
503 | mem_free (uc, sizeof (struct udp_cache)); | |
e7fd8a39 UD |
504 | CACHE_PERROR (_("enablecache: could not allocate cache fifo")); |
505 | return 0; | |
506 | } | |
e7fd8a39 UD |
507 | su->su_cache = (char *) uc; |
508 | return 1; | |
28f540f4 | 509 | } |
021db4be | 510 | libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0) |
28f540f4 RM |
511 | |
512 | ||
513 | /* | |
514 | * Set an entry in the cache | |
515 | */ | |
e7fd8a39 UD |
516 | static void |
517 | cache_set (SVCXPRT *xprt, u_long replylen) | |
28f540f4 | 518 | { |
e7fd8a39 UD |
519 | cache_ptr victim; |
520 | cache_ptr *vicp; | |
521 | struct svcudp_data *su = su_data (xprt); | |
522 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
523 | u_int loc; | |
524 | char *newbuf; | |
525 | ||
526 | /* | |
527 | * Find space for the new entry, either by | |
528 | * reusing an old entry, or by mallocing a new one | |
529 | */ | |
530 | victim = uc->uc_fifo[uc->uc_nextvictim]; | |
531 | if (victim != NULL) | |
532 | { | |
533 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
534 | for (vicp = &uc->uc_entries[loc]; | |
535 | *vicp != NULL && *vicp != victim; | |
536 | vicp = &(*vicp)->cache_next) | |
537 | ; | |
538 | if (*vicp == NULL) | |
539 | { | |
540 | CACHE_PERROR (_("cache_set: victim not found")); | |
541 | return; | |
28f540f4 | 542 | } |
e7fd8a39 UD |
543 | *vicp = victim->cache_next; /* remote from cache */ |
544 | newbuf = victim->cache_reply; | |
545 | } | |
546 | else | |
547 | { | |
548 | victim = ALLOC (struct cache_node, 1); | |
549 | if (victim == NULL) | |
550 | { | |
551 | CACHE_PERROR (_("cache_set: victim alloc failed")); | |
552 | return; | |
553 | } | |
554 | newbuf = mem_alloc (su->su_iosz); | |
555 | if (newbuf == NULL) | |
556 | { | |
0292b0dd | 557 | mem_free (victim, sizeof (struct cache_node)); |
e7fd8a39 UD |
558 | CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer")); |
559 | return; | |
560 | } | |
561 | } | |
562 | ||
563 | /* | |
564 | * Store it away | |
565 | */ | |
566 | victim->cache_replylen = replylen; | |
567 | victim->cache_reply = rpc_buffer (xprt); | |
568 | rpc_buffer (xprt) = newbuf; | |
7b57bfe5 | 569 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE); |
e7fd8a39 UD |
570 | victim->cache_xid = su->su_xid; |
571 | victim->cache_proc = uc->uc_proc; | |
572 | victim->cache_vers = uc->uc_vers; | |
573 | victim->cache_prog = uc->uc_prog; | |
574 | victim->cache_addr = uc->uc_addr; | |
575 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
576 | victim->cache_next = uc->uc_entries[loc]; | |
577 | uc->uc_entries[loc] = victim; | |
578 | uc->uc_fifo[uc->uc_nextvictim++] = victim; | |
579 | uc->uc_nextvictim %= uc->uc_size; | |
28f540f4 RM |
580 | } |
581 | ||
582 | /* | |
583 | * Try to get an entry from the cache | |
584 | * return 1 if found, 0 if not found | |
585 | */ | |
e7fd8a39 | 586 | static int |
f63f2bfd JM |
587 | cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, |
588 | u_long *replylenp) | |
28f540f4 | 589 | { |
e7fd8a39 UD |
590 | u_int loc; |
591 | cache_ptr ent; | |
592 | struct svcudp_data *su = su_data (xprt); | |
593 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
594 | ||
50304ef0 | 595 | #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) |
e7fd8a39 UD |
596 | |
597 | loc = CACHE_LOC (xprt, su->su_xid); | |
598 | for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) | |
599 | { | |
600 | if (ent->cache_xid == su->su_xid && | |
601 | ent->cache_proc == uc->uc_proc && | |
602 | ent->cache_vers == uc->uc_vers && | |
603 | ent->cache_prog == uc->uc_prog && | |
604 | EQADDR (ent->cache_addr, uc->uc_addr)) | |
605 | { | |
606 | *replyp = ent->cache_reply; | |
607 | *replylenp = ent->cache_replylen; | |
608 | return 1; | |
28f540f4 | 609 | } |
e7fd8a39 UD |
610 | } |
611 | /* | |
612 | * Failed to find entry | |
613 | * Remember a few things so we can do a set later | |
614 | */ | |
615 | uc->uc_proc = msg->rm_call.cb_proc; | |
616 | uc->uc_vers = msg->rm_call.cb_vers; | |
617 | uc->uc_prog = msg->rm_call.cb_prog; | |
c1301d9a | 618 | memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr)); |
e7fd8a39 | 619 | return 0; |
28f540f4 | 620 | } |