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